From d8151a286694ba76726448cad92b1f264fc00e3b Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Mon, 2 Dec 2024 12:49:32 +0100 Subject: [PATCH 01/16] feat(node-utils): checkSocks5Proxy util --- packages/node-utils/src/checkSocks5Proxy.ts | 35 +++++++++++++++++++++ packages/node-utils/src/index.ts | 1 + 2 files changed, 36 insertions(+) create mode 100644 packages/node-utils/src/checkSocks5Proxy.ts diff --git a/packages/node-utils/src/checkSocks5Proxy.ts b/packages/node-utils/src/checkSocks5Proxy.ts new file mode 100644 index 00000000000..e6af1f4e138 --- /dev/null +++ b/packages/node-utils/src/checkSocks5Proxy.ts @@ -0,0 +1,35 @@ +import net from 'net'; + +export const checkSocks5Proxy = (host: string, port: number): Promise => { + return new Promise((resolve, reject) => { + const socket = new net.Socket(); + + socket.setTimeout(2_000); + + socket.on('connect', () => { + // Version 5, 1 method, no authentication + const handshakeRequest = Buffer.from([0x05, 0x01, 0x00]); + socket.write(handshakeRequest); + }); + + socket.on('data', data => { + if (data[0] === 0x05 && data[1] === 0x00) { + resolve(true); + } else { + resolve(false); + } + socket.destroy(); + }); + + socket.on('error', err => { + reject(err); + }); + + socket.on('timeout', () => { + socket.destroy(); + reject(new Error('Connection timed out')); + }); + + socket.connect(port, host); + }); +}; diff --git a/packages/node-utils/src/index.ts b/packages/node-utils/src/index.ts index 0201677b904..951c43c5d5e 100644 --- a/packages/node-utils/src/index.ts +++ b/packages/node-utils/src/index.ts @@ -12,3 +12,4 @@ export { type Response, } from './http'; export { checkFileExists } from './checkFileExists'; +export { checkSocks5Proxy } from './checkSocks5Proxy'; From 2844d98550e416fe577e68003188898ed801c10b Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Mon, 2 Dec 2024 12:49:59 +0100 Subject: [PATCH 02/16] test(node-utils): checkSocks5Proxy tests --- .../src/tests/checkSocks5Proxy.test.ts | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 packages/node-utils/src/tests/checkSocks5Proxy.test.ts diff --git a/packages/node-utils/src/tests/checkSocks5Proxy.test.ts b/packages/node-utils/src/tests/checkSocks5Proxy.test.ts new file mode 100644 index 00000000000..7a90e28ae78 --- /dev/null +++ b/packages/node-utils/src/tests/checkSocks5Proxy.test.ts @@ -0,0 +1,62 @@ +import net from 'net'; + +import { checkSocks5Proxy } from '../checkSocks5Proxy'; + +jest.mock('net'); + +describe('checkSocks5Proxy', () => { + const host = '127.0.0.1'; + const port = 9050; + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should return true for a valid SOCKS5 proxy', async () => { + const mockSocket = { + connect: jest.fn(), + write: jest.fn(), + on: jest.fn((event, callback) => { + if (event === 'connect') { + callback(); + } + if (event === 'data') { + // Valid SOCKS5 response. + callback(Buffer.from([0x05, 0x00])); + } + }), + setTimeout: jest.fn(), + destroy: jest.fn(), + }; + + // @ts-expect-error + net.Socket.mockImplementation(() => mockSocket); + + const result = await checkSocks5Proxy(host, port); + expect(result).toBe(true); + }); + + it('should return false for an invalid SOCKS5 proxy', async () => { + const mockSocket = { + connect: jest.fn(), + write: jest.fn(), + on: jest.fn((event, callback) => { + if (event === 'connect') { + callback(); + } + if (event === 'data') { + // Not valid SOCKS5 response + callback(Buffer.from([0x05, 0x01])); + } + }), + setTimeout: jest.fn(), + destroy: jest.fn(), + }; + + // @ts-expect-error + net.Socket.mockImplementation(() => mockSocket); + + const result = await checkSocks5Proxy(host, port); + expect(result).toBe(false); + }); +}); From 4490ad66482a40fdaef26fb7aaec6efa563bf575 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Mon, 2 Dec 2024 12:52:03 +0100 Subject: [PATCH 03/16] feat(request-manager): support external Tor --- packages/request-manager/src/controller.ts | 106 +++++++++--------- .../request-manager/src/controllerExternal.ts | 99 ++++++++++++++++ packages/request-manager/src/index.ts | 1 + packages/request-manager/src/types.ts | 6 + 4 files changed, 158 insertions(+), 54 deletions(-) create mode 100644 packages/request-manager/src/controllerExternal.ts diff --git a/packages/request-manager/src/controller.ts b/packages/request-manager/src/controller.ts index ac42aeb48c8..ec1314354fd 100644 --- a/packages/request-manager/src/controller.ts +++ b/packages/request-manager/src/controller.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; import path from 'path'; -import { createTimeoutPromise } from '@trezor/utils'; +import { ScheduleActionParams, ScheduledAction, scheduleAction } from '@trezor/utils'; import { checkFileExists } from '@trezor/node-utils'; import { TorControlPort } from './torControlPort'; @@ -13,15 +13,15 @@ import { } from './types'; import { bootstrapParser, BOOTSTRAP_EVENT_PROGRESS } from './events/bootstrap'; +const WAITING_TIME = 1000; +const MAX_TRIES_WAITING = 200; +const BOOTSTRAP_SLOW_TRESHOLD = 1000 * 5; // 5 seconds. + export class TorController extends EventEmitter { options: TorConnectionOptions; controlPort: TorControlPort; bootstrapSlownessChecker?: NodeJS.Timeout; status: TorControllerStatus = TOR_CONTROLLER_STATUS.Stopped; - // Configurations - waitingTime = 1000; - maxTriesWaiting = 200; - bootstrapSlowThreshold = 1000 * 5; // 5 seconds. constructor(options: TorConnectionOptions) { super(); @@ -57,14 +57,32 @@ export class TorController extends EventEmitter { if (this.bootstrapSlownessChecker) { clearTimeout(this.bootstrapSlownessChecker); } - // When Bootstrap starts we wait time defined in bootstrapSlowThreshold and if after that time, + // When Bootstrap starts we wait time defined in BOOTSTRAP_SLOW_TRESHOLD and if after that time, // it has not being finalized, then we send slow event. We know that Bootstrap is going on since // we received, at least, first Bootstrap events from ControlPort. this.bootstrapSlownessChecker = setTimeout(() => { this.emit('bootstrap/event', { type: 'slow', }); - }, this.bootstrapSlowThreshold); + }, BOOTSTRAP_SLOW_TRESHOLD); + } + + private onMessageReceived(message: string) { + const bootstrap: BootstrapEvent[] = bootstrapParser(message); + bootstrap.forEach(event => { + if (event.type !== 'progress') return; + if (event.progress && !this.getIsBootstrapping()) { + // We consider that bootstrap has started when we receive any bootstrap event and + // Tor is not bootstrapping yet. + // If we do not receive any bootstrapping event, we can consider there is something going wrong and + // an error will be thrown when `MAX_TRIES_WAITING` is reached in `waitUntilAlive`. + this.startBootstrap(); + } + if (event.progress === BOOTSTRAP_EVENT_PROGRESS.Done) { + this.successfullyBootstrapped(); + } + this.emit('bootstrap/event', event); + }); } public async getTorConfiguration( @@ -173,59 +191,39 @@ export class TorController extends EventEmitter { return config; } - public onMessageReceived(message: string) { - const bootstrap: BootstrapEvent[] = bootstrapParser(message); - bootstrap.forEach(event => { - if (event.type !== 'progress') return; - if (event.progress && !this.getIsBootstrapping()) { - // We consider that bootstrap has started when we receive any bootstrap event and - // Tor is not bootstrapping yet. - // If we do not receive any bootstrapping event, we can consider there is something going wrong and - // an error will be thrown when `maxTriesWaiting` is reached in `waitUntilAlive`. - this.startBootstrap(); - } - if (event.progress === BOOTSTRAP_EVENT_PROGRESS.Done) { - this.successfullyBootstrapped(); - } - this.emit('bootstrap/event', event); - }); - } - - public waitUntilAlive(): Promise { - const errorMessages: string[] = []; + public async waitUntilAlive(): Promise { this.status = TOR_CONTROLLER_STATUS.Bootstrapping; - const waitUntilResponse = async (triesCount: number): Promise => { - if (this.getIsStopped()) { - // If TOR is starting and we want to cancel it. - return; + + const abortController = new AbortController(); + const checkConnection: ScheduledAction = async signal => { + if (signal?.aborted) { + throw new Error('Tor controller check alive aborted'); } - if (triesCount >= this.maxTriesWaiting) { - throw new Error( - `Timeout waiting for TOR control port: \n${errorMessages.join('\n')}`, - ); + const isConnected = await this.controlPort.connect(); + const isAlive = this.controlPort.ping(); + const isCircuitEstablished = this.getIsCircuitEstablished(); + // It is running so let's not wait anymore. + if (isConnected && isAlive && isCircuitEstablished) { + return true; } - try { - const isConnected = await this.controlPort.connect(); - const isAlive = this.controlPort.ping(); - if (isConnected && isAlive && this.getIsCircuitEstablished()) { - // It is running so let's not wait anymore. - return; - } - } catch (error) { - // Some error here is expected when waiting but - // we do not want to throw until maxTriesWaiting is reach. - // Instead we want to log it to know what causes the error. - if (error && error.message) { - console.warn('request-manager:', error.message); - errorMessages.push(error.message); + throw new Error('Tor not alive'); + }; + const params: ScheduleActionParams = { + attempts: MAX_TRIES_WAITING, + timeout: WAITING_TIME, + gap: WAITING_TIME, + signal: abortController.signal, + attemptFailureHandler: () => { + if (this.getIsStopped()) { + abortController.abort(); + + return new Error('Operation stopped.'); } - } - await createTimeoutPromise(this.waitingTime); - return waitUntilResponse(triesCount + 1); + return undefined; + }, }; - - return waitUntilResponse(1); + await scheduleAction(checkConnection, params); } public getStatus(): Promise { diff --git a/packages/request-manager/src/controllerExternal.ts b/packages/request-manager/src/controllerExternal.ts new file mode 100644 index 00000000000..8e81d701e43 --- /dev/null +++ b/packages/request-manager/src/controllerExternal.ts @@ -0,0 +1,99 @@ +import { EventEmitter } from 'events'; + +import { ScheduleActionParams, ScheduledAction, scheduleAction } from '@trezor/utils'; +import { checkSocks5Proxy } from '@trezor/node-utils'; + +import { TOR_CONTROLLER_STATUS, TorControllerStatus, TorExternalConnectionOptions } from './types'; + +const WAITING_TIME = 1_000; +const MAX_TRIES_WAITING = 200; + +export class TorControllerExternal extends EventEmitter { + status: TorControllerStatus = TOR_CONTROLLER_STATUS.Stopped; + options: TorExternalConnectionOptions; + + constructor(options: TorExternalConnectionOptions) { + super(); + this.options = options; + } + + private getIsStopped() { + return this.status === TOR_CONTROLLER_STATUS.Stopped; + } + + private async getIsExternalTorRunning() { + let isSocks5ProxyPort = false; + try { + isSocks5ProxyPort = await checkSocks5Proxy(this.options.host, this.options.port); + } catch { + // Ignore errors. + } + + return isSocks5ProxyPort; + } + + private startBootstrap() { + this.status = TOR_CONTROLLER_STATUS.Bootstrapping; + } + + private successfullyBootstrapped() { + this.status = TOR_CONTROLLER_STATUS.ExternalTorRunning; + } + + public getTorConfiguration() { + return ''; + } + + public async waitUntilAlive() { + this.startBootstrap(); + + const abortController = new AbortController(); + const checkConnection: ScheduledAction = async signal => { + if (signal?.aborted) { + throw new Error('Operation aborted'); + } + const isRunning = await this.getIsExternalTorRunning(); + if (isRunning) { + this.successfullyBootstrapped(); + + return true; + } + + throw new Error('Tor external not alive'); + }; + + const params: ScheduleActionParams = { + attempts: MAX_TRIES_WAITING, + timeout: WAITING_TIME, + gap: WAITING_TIME, + signal: abortController.signal, + attemptFailureHandler: () => { + if (this.getIsStopped()) { + abortController.abort(); + + return new Error('Operation stopped.'); + } + + return undefined; + }, + }; + await scheduleAction(checkConnection, params); + } + + public async getStatus() { + const isExternalTorRunning = await this.getIsExternalTorRunning(); + if (isExternalTorRunning) { + return TOR_CONTROLLER_STATUS.ExternalTorRunning; + } + + return TOR_CONTROLLER_STATUS.Stopped; + } + + public closeActiveCircuits() { + // Do nothing. Not possible in External Tor without ControlPort. + } + + public stop() { + this.status = TOR_CONTROLLER_STATUS.Stopped; + } +} diff --git a/packages/request-manager/src/index.ts b/packages/request-manager/src/index.ts index aef9257d339..2e198e5fe49 100644 --- a/packages/request-manager/src/index.ts +++ b/packages/request-manager/src/index.ts @@ -1,4 +1,5 @@ export { TorController } from './controller'; +export { TorControllerExternal } from './controllerExternal'; export { createInterceptor } from './interceptor'; export type { InterceptedEvent, BootstrapEvent, TorControllerStatus } from './types'; export { TOR_CONTROLLER_STATUS } from './types'; diff --git a/packages/request-manager/src/types.ts b/packages/request-manager/src/types.ts index ac9fff30985..c567100c445 100644 --- a/packages/request-manager/src/types.ts +++ b/packages/request-manager/src/types.ts @@ -6,6 +6,11 @@ export interface TorConnectionOptions { snowflakeBinaryPath: string; } +export interface TorExternalConnectionOptions { + host: string; + port: number; +} + export type TorCommandResponse = | { success: true; @@ -72,5 +77,6 @@ export const TOR_CONTROLLER_STATUS = { Bootstrapping: 'Bootstrapping', Stopped: 'Stopped', CircuitEstablished: 'CircuitEstablished', + ExternalTorRunning: 'ExternalTorRunning', } as const; export type TorControllerStatus = keyof typeof TOR_CONTROLLER_STATUS; From 82be1c10ea895af97df77320fd7bc74374855502 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Mon, 2 Dec 2024 12:53:02 +0100 Subject: [PATCH 04/16] feat(suite-desktop-core): support external Tor --- packages/suite-desktop-core/src/index.d.ts | 5 +- .../src/libs/processes/TorExternalProcess.ts | 46 +++++ .../src/libs/processes/TorProcess.ts | 10 +- packages/suite-desktop-core/src/libs/store.ts | 3 + .../suite-desktop-core/src/modules/tor.ts | 159 +++++++++++++----- .../src/modules/trezor-connect.ts | 11 +- 6 files changed, 184 insertions(+), 50 deletions(-) create mode 100644 packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts diff --git a/packages/suite-desktop-core/src/index.d.ts b/packages/suite-desktop-core/src/index.d.ts index 3c39c93cfb3..64d3b1b6a4a 100644 --- a/packages/suite-desktop-core/src/index.d.ts +++ b/packages/suite-desktop-core/src/index.d.ts @@ -100,8 +100,11 @@ declare type UpdateSettings = { declare type TorSettings = { running: boolean; // Tor should be enabled host: string; // Hostname of the tor process through which traffic is routed - port: number; // Port of the tor process through which traffic is routed + port: number; // Port of the Tor process through which traffic is routed + controlPort: number; // Port of the Tor Control Port + torDataDir: string; // Path of tor data directory snowflakeBinaryPath: string; // Path in user system to the snowflake binary + useExternalTor: boolean; // Tor should use external daemon instead of the one built-in suite. }; declare type BridgeSettings = { diff --git a/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts b/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts new file mode 100644 index 00000000000..76d8fb152ae --- /dev/null +++ b/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts @@ -0,0 +1,46 @@ +import { TOR_CONTROLLER_STATUS, TorControllerExternal } from '@trezor/request-manager'; + +import { Status } from './BaseProcess'; + +export type TorProcessStatus = Status & { isBootstrapping?: boolean }; + +const DEFAULT_TOR_EXTERNAL_HOST = '127.0.0.1'; +const DEFAULT_TOR_EXTERNAL_PORT = 9050; + +export class TorExternalProcess { + isStopped = true; + torController: TorControllerExternal; + port = DEFAULT_TOR_EXTERNAL_PORT; + host = DEFAULT_TOR_EXTERNAL_HOST; + constructor() { + this.torController = new TorControllerExternal({ host: this.host, port: this.port }); + } + + public setTorConfig(_torConfig: { useExternalTor: boolean; snowflakeBinaryPath: string }) { + // Do nothing + } + + public getPort() { + return this.port; + } + + public async status(): Promise { + const torControllerStatus = await this.torController.getStatus(); + + return { + service: torControllerStatus === TOR_CONTROLLER_STATUS.ExternalTorRunning, + process: torControllerStatus === TOR_CONTROLLER_STATUS.ExternalTorRunning, + isBootstrapping: false, // For Tor external we fake bootstrap process. + }; + } + + public async start(): Promise { + this.isStopped = false; + await this.torController.waitUntilAlive(); + } + + public stop() { + // We should not stop External Tor Process but ignore it. + this.isStopped = true; + } +} diff --git a/packages/suite-desktop-core/src/libs/processes/TorProcess.ts b/packages/suite-desktop-core/src/libs/processes/TorProcess.ts index 6ac1d5b5622..97b7183f5a8 100644 --- a/packages/suite-desktop-core/src/libs/processes/TorProcess.ts +++ b/packages/suite-desktop-core/src/libs/processes/TorProcess.ts @@ -31,11 +31,15 @@ export class TorProcess extends BaseProcess { }); } - setTorConfig(torConfig: Pick) { + public setTorConfig(torConfig: Pick) { this.snowflakeBinaryPath = torConfig.snowflakeBinaryPath; } - async status(): Promise { + public getPort() { + return this.port; + } + + public async status(): Promise { const torControllerStatus = await this.torController.getStatus(); return { @@ -45,7 +49,7 @@ export class TorProcess extends BaseProcess { }; } - async start(): Promise { + public async start(): Promise { const electronProcessId = process.pid; const torConfiguration = await this.torController.getTorConfiguration( electronProcessId, diff --git a/packages/suite-desktop-core/src/libs/store.ts b/packages/suite-desktop-core/src/libs/store.ts index f8a5cd9e02d..67700c2b90f 100644 --- a/packages/suite-desktop-core/src/libs/store.ts +++ b/packages/suite-desktop-core/src/libs/store.ts @@ -63,8 +63,11 @@ export class Store { return this.store.get('torSettings', { running: false, port: 9050, + controlPort: 9051, host: '127.0.0.1', snowflakeBinaryPath: '', + useExternalTor: false, + torDataDir: '', }); } diff --git a/packages/suite-desktop-core/src/modules/tor.ts b/packages/suite-desktop-core/src/modules/tor.ts index 5c3ca1b5c3c..cde6a58003b 100644 --- a/packages/suite-desktop-core/src/modules/tor.ts +++ b/packages/suite-desktop-core/src/modules/tor.ts @@ -12,28 +12,52 @@ import { getFreePort } from '@trezor/node-utils'; import { validateIpcMessage } from '@trezor/ipc-proxy'; import { TorProcess, TorProcessStatus } from '../libs/processes/TorProcess'; +import { TorExternalProcess } from '../libs/processes/TorExternalProcess'; import { app, ipcMain } from '../typed-electron'; import type { Dependencies } from './index'; const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) => { const { logger } = global; - const host = '127.0.0.1'; - const port = await getFreePort(); - const controlPort = await getFreePort(); - const torDataDir = path.join(app.getPath('userData'), 'tor'); const initialSettings = store.getTorSettings(); - store.setTorSettings({ ...initialSettings, host, port }); - - const tor = new TorProcess({ - host, - port, - controlPort, - torDataDir, - snowflakeBinaryPath: initialSettings.snowflakeBinaryPath, + store.setTorSettings({ + ...initialSettings, + port: await getFreePort(), + controlPort: await getFreePort(), + torDataDir: path.join(app.getPath('userData'), 'tor'), }); + const settings = store.getTorSettings(); + + const processes = [ + { + type: 'tor', + process: new TorProcess({ + host: settings.host, + port: settings.port, + controlPort: settings.controlPort, + torDataDir: settings.torDataDir, + snowflakeBinaryPath: settings.snowflakeBinaryPath, + }), + }, + { + type: 'tor-external', + process: new TorExternalProcess(), + }, + ]; + + const getTarget = () => { + const { useExternalTor } = store.getTorSettings(); + const currentTarget = useExternalTor ? 'tor-external' : 'tor'; + + return processes.find(process => process.type === currentTarget)!.process; + }; + + const updateTorPort = (port: number) => { + store.setTorSettings({ ...store.getTorSettings(), port }); + }; + const setProxy = (rule: string) => { logger.info('tor', `Setting proxy rules to "${rule}"`); // Including network session of electron auto-updater in the Tor proxy. @@ -44,17 +68,26 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) }); }; - const getProxySettings = (shouldEnableTor: boolean) => - shouldEnableTor ? { proxy: `socks://${host}:${port}` } : { proxy: '' }; + const getProxySettings = (shouldEnableTor: boolean) => { + const { useExternalTor, port, host } = store.getTorSettings(); + return shouldEnableTor + ? { + proxy: `socks://${host}:${useExternalTor ? 9050 : port}`, + } + : { proxy: '' }; + }; const handleTorProcessStatus = (status: TorProcessStatus) => { + const { useExternalTor, running } = store.getTorSettings(); let type: TorStatus; if (!status.process) { type = TorStatus.Disabled; } else if (status.isBootstrapping) { type = TorStatus.Enabling; - } else if (status.service) { + } else if (status.service && !useExternalTor) { + type = TorStatus.Enabled; + } else if (useExternalTor && running) { type = TorStatus.Enabled; } else { type = TorStatus.Disabled; @@ -90,20 +123,52 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) } }; + const createFakeBootstrapProcess = () => { + let progress = 0; + const duration = 3_000; + // update progress every 300ms. + const interval = 300; + + const increment = (100 / duration) * interval; + const intervalId = setInterval(() => { + progress += increment; + if (progress >= 100) { + progress = 100; + clearInterval(intervalId); + } + handleBootstrapEvent({ + type: 'progress', + progress: `${progress}`, + summary: 'Using External Tor fake progress', + }); + }, interval); + }; + const setupTor = async (shouldEnableTor: boolean) => { - const isTorRunning = (await tor.status()).process; - const { snowflakeBinaryPath } = store.getTorSettings(); + const { useExternalTor, snowflakeBinaryPath } = store.getTorSettings(); + + const isTorRunning = (await getTarget().status()).process; - if (shouldEnableTor === isTorRunning) { + if (shouldEnableTor === isTorRunning && !useExternalTor) { return; } if (shouldEnableTor === true) { - setProxy(`socks5://${host}:${port}`); - tor.torController.on('bootstrap/event', handleBootstrapEvent); + const { host } = store.getTorSettings(); + const port = getTarget().getPort(); + const proxyRule = `socks5://${host}:${port}`; + setProxy(proxyRule); + getTarget().torController.on('bootstrap/event', handleBootstrapEvent); + try { - tor.setTorConfig({ snowflakeBinaryPath }); - await tor.start(); + getTarget().setTorConfig({ snowflakeBinaryPath, useExternalTor }); + updateTorPort(port); + if (useExternalTor) { + await getTarget().start(); + createFakeBootstrapProcess(); + } else { + await getTarget().start(); + } } catch (error) { mainWindowProxy.getInstance()?.webContents.send('tor/bootstrap', { type: 'error', @@ -111,18 +176,19 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) }); // When there is error does not mean that the process is stop, // so we make sure to stop it so we are able to restart it. - tor.stop(); + getTarget().stop(); + throw error; } finally { - tor.torController.removeAllListeners(); + getTarget().torController.removeAllListeners(); } } else { mainWindowProxy.getInstance()?.webContents.send('tor/status', { type: TorStatus.Disabling, }); setProxy(''); - tor.torController.stop(); - await tor.stop(); + getTarget().torController.stop(); + await getTarget().stop(); } store.setTorSettings({ ...store.getTorSettings(), running: shouldEnableTor }); @@ -130,15 +196,20 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) ipcMain.handle( 'tor/change-settings', - (ipcEvent, { snowflakeBinaryPath }: { snowflakeBinaryPath: string }) => { + ( + ipcEvent, + { + snowflakeBinaryPath, + useExternalTor, + }: { snowflakeBinaryPath: string; useExternalTor: boolean }, + ) => { validateIpcMessage(ipcEvent); try { store.setTorSettings({ - running: store.getTorSettings().running, - host, - port, + ...store.getTorSettings(), snowflakeBinaryPath, + useExternalTor, }); return { success: true }; @@ -175,6 +246,7 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) // correctly set in module trezor-connect-ipc. const proxySettings = getProxySettings(shouldEnableTor); + // Proxy is also set in packages/suite-desktop-core/src/modules/trezor-connect.ts await TrezorConnect.setProxy(proxySettings); logger.info( @@ -201,7 +273,8 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) } // Once Tor is toggled it renderer should know the new status. - const status = await tor.status(); + const status = await getTarget().status(); + handleTorProcessStatus(status); return { success: true }; @@ -211,11 +284,16 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) let lastCircuitResetTime = 0; const socksTimeout = 30000; // this value reflects --SocksTimeout flag set by TorController config mainThreadEmitter.on('module/reset-tor-circuits', event => { + if (store.getTorSettings().useExternalTor) { + logger.debug('tor', `Ignore circuit reset. Running External Tor without Control Port.`); + + return; + } const lastResetDiff = Date.now() - lastCircuitResetTime; if (lastResetDiff > socksTimeout) { logger.debug('tor', `Close active circuits. Triggered by identity ${event.identity}`); lastCircuitResetTime = Date.now(); - tor.torController.closeActiveCircuits(); + getTarget().torController.closeActiveCircuits(); } else { logger.debug( 'tor', @@ -225,8 +303,9 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) }); ipcMain.on('tor/get-status', async () => { - logger.debug('tor', `Getting status (${store.getTorSettings().running ? 'ON' : 'OFF'})`); - const status = await tor.status(); + const { running } = store.getTorSettings(); + logger.debug('tor', `Getting status (${running ? 'ON' : 'OFF'})`); + const status = await getTarget().status(); handleTorProcessStatus(status); }); @@ -235,7 +314,7 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) store.setTorSettings({ ...store.getTorSettings(), running: true }); } - return tor; + return getTarget; }; type TorModule = (dependencies: Dependencies) => { @@ -245,24 +324,24 @@ type TorModule = (dependencies: Dependencies) => { export const init: TorModule = dependencies => { let loaded = false; - let tor: TorProcess | undefined; + let getTarget: any; const onLoad = async () => { if (loaded) return { shouldRunTor: false }; loaded = true; - tor = await load(dependencies); - const torSettings = dependencies.store.getTorSettings(); + getTarget = await load(dependencies); + const { running } = dependencies.store.getTorSettings(); return { - shouldRunTor: torSettings.running, + shouldRunTor: running, }; }; const onQuit = async () => { const { logger } = global; logger.info('tor', 'Stopping (app quit)'); - await tor?.stop(); + await getTarget()?.stop(); }; return { onLoad, onQuit }; diff --git a/packages/suite-desktop-core/src/modules/trezor-connect.ts b/packages/suite-desktop-core/src/modules/trezor-connect.ts index eb8c1d375cf..393e13896dc 100644 --- a/packages/suite-desktop-core/src/modules/trezor-connect.ts +++ b/packages/suite-desktop-core/src/modules/trezor-connect.ts @@ -11,11 +11,10 @@ export const initBackground: ModuleInitBackground = ({ mainThreadEmitter, store const { logger } = global; logger.info(SERVICE_NAME, `Starting service`); - const setProxy = (ifRunning = false) => { - const tor = store.getTorSettings(); - if (ifRunning && !tor.running) return Promise.resolve(); - const payload = tor.running ? { proxy: `socks://${tor.host}:${tor.port}` } : { proxy: '' }; - logger.info(SERVICE_NAME, `${tor.running ? 'Enable' : 'Disable'} proxy ${payload.proxy}`); + const setProxy = () => { + const { running, host, port } = store.getTorSettings(); + const payload = running ? { proxy: `socks://${host}:${port}` } : { proxy: '' }; + logger.info(SERVICE_NAME, `${running ? 'Enable' : 'Disable'} proxy ${payload.proxy}`); return TrezorConnect.setProxy(payload); }; @@ -26,7 +25,7 @@ export const initBackground: ModuleInitBackground = ({ mainThreadEmitter, store logger.debug(SERVICE_NAME, `call ${method}`); if (method === 'init') { const response = await TrezorConnect[method](...params); - await setProxy(true); + await setProxy(); return response; } From febf788d72f7e924474f61ecce42bf41ee61d1b4 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Mon, 2 Dec 2024 12:53:28 +0100 Subject: [PATCH 05/16] feat(suite-desktop-api): support external Tor --- packages/suite-desktop-api/src/messages.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/suite-desktop-api/src/messages.ts b/packages/suite-desktop-api/src/messages.ts index 6eeb54c9562..24f0cc42059 100644 --- a/packages/suite-desktop-api/src/messages.ts +++ b/packages/suite-desktop-api/src/messages.ts @@ -55,6 +55,7 @@ export type HandshakeTorModule = { export type TorSettings = { snowflakeBinaryPath: string; + useExternalTor: boolean; }; export type TraySettings = { From 8e2cbed1681b8fde9e85f6b942fce1d37a6d4087 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Mon, 2 Dec 2024 12:53:58 +0100 Subject: [PATCH 06/16] feat(suite): external-tor experimental feature --- .../suite/src/constants/suite/experimental.ts | 17 ++++++++++++++++- packages/suite/src/support/messages.ts | 9 +++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/suite/src/constants/suite/experimental.ts b/packages/suite/src/constants/suite/experimental.ts index 45e6721e889..1c96d8b7fd8 100644 --- a/packages/suite/src/constants/suite/experimental.ts +++ b/packages/suite/src/constants/suite/experimental.ts @@ -5,7 +5,7 @@ import { Route } from '@suite-common/suite-types'; import { Dispatch } from '../../types/suite'; -export type ExperimentalFeature = 'password-manager' | 'tor-snowflake'; +export type ExperimentalFeature = 'password-manager' | 'tor-snowflake' | 'tor-external'; export type ExperimentalFeatureConfig = { title: TranslationKey; @@ -39,4 +39,19 @@ export const EXPERIMENTAL_FEATURES: Record { + const result = await desktopApi.getTorSettings(); + if (result.success && result.payload.useExternalTor !== newValue) { + await desktopApi.changeTorSettings({ + ...result.payload, + useExternalTor: newValue, + }); + } + }, + }, }; diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index 388e72c5b3b..ea3da7d84be 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -5010,6 +5010,15 @@ export default defineMessages({ defaultMessage: 'Access censored websites and apps using Tor Snowflake, a system designed to bypass restrictions.', }, + TR_EXPERIMENTAL_TOR_EXTERNAL: { + id: 'TR_EXPERIMENTAL_TOR_EXTERNAL', + defaultMessage: 'Tor external', + }, + TR_EXPERIMENTAL_TOR_EXTERNAL_DESCRIPTION: { + id: 'TR_EXPERIMENTAL_TOR_EXTERNAL_DESCRIPTION', + defaultMessage: + 'Allows you to use Tor daemon running in a external process on port 9050 instead of the one bundled with Trezor Suite.', + }, TR_EARLY_ACCESS: { id: 'TR_EARLY_ACCESS', defaultMessage: 'Early Access Program', From f1864ecb4a8c1e8ac577a86944c83133bb3e59ef Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 08:59:28 +0100 Subject: [PATCH 07/16] chore(request-manager): remove snowflake direct support --- .../request-manager/e2e/identities-stress.ts | 1 - .../request-manager/e2e/interceptor.test.ts | 4 +- .../e2e/torControlPort.test.ts | 3 -- packages/request-manager/src/controller.ts | 41 +------------------ packages/request-manager/src/types.ts | 1 - 5 files changed, 2 insertions(+), 48 deletions(-) diff --git a/packages/request-manager/e2e/identities-stress.ts b/packages/request-manager/e2e/identities-stress.ts index 5f1289c8c53..ca449f5bb3d 100644 --- a/packages/request-manager/e2e/identities-stress.ts +++ b/packages/request-manager/e2e/identities-stress.ts @@ -37,7 +37,6 @@ const intervalBetweenRequests = 1000 * 20; port, controlPort, torDataDir, - snowflakeBinaryPath: '', }); const torParams = await torController.getTorConfiguration(processId); // Starting Tor process from binary. diff --git a/packages/request-manager/e2e/interceptor.test.ts b/packages/request-manager/e2e/interceptor.test.ts index dd12649c141..c47747ef58c 100644 --- a/packages/request-manager/e2e/interceptor.test.ts +++ b/packages/request-manager/e2e/interceptor.test.ts @@ -10,7 +10,6 @@ const hostIp = '127.0.0.1'; const port = 38835; const controlPort = 35527; const processId = process.pid; -const snowflakeBinaryPath = ''; // 1 minute before timeout, because Tor might be slow to start. jest.setTimeout(60000); @@ -29,7 +28,7 @@ describe('Interceptor', () => { let torController: TorController; let torIdentities: TorIdentities; - const torSettings = { running: true, host: hostIp, port, snowflakeBinaryPath }; + const torSettings = { running: true, host: hostIp, port }; const INTERCEPTOR = { handler: () => {}, @@ -45,7 +44,6 @@ describe('Interceptor', () => { port, controlPort, torDataDir, - snowflakeBinaryPath, }); const torParams = await torController.getTorConfiguration(processId); // Starting Tor process from binary. diff --git a/packages/request-manager/e2e/torControlPort.test.ts b/packages/request-manager/e2e/torControlPort.test.ts index d341d062f13..864764c11ad 100644 --- a/packages/request-manager/e2e/torControlPort.test.ts +++ b/packages/request-manager/e2e/torControlPort.test.ts @@ -18,7 +18,6 @@ const controlAuthCookiePath = `${torDataDir}/control_auth_cookie`; const host = 'localhost'; const port = 9998; const controlPort = 9999; -const snowflakeBinaryPath = ''; describe('TorControlPort', () => { beforeAll(async () => { @@ -40,7 +39,6 @@ describe('TorControlPort', () => { port, controlPort, torDataDir, - snowflakeBinaryPath, }; const fakeListener = () => {}; const torControlPort = new TorControlPort(options, fakeListener); @@ -105,7 +103,6 @@ describe('TorControlPort', () => { port, controlPort, torDataDir, - snowflakeBinaryPath, }; const fakeListener = () => {}; const torControlPort = new TorControlPort(options, fakeListener); diff --git a/packages/request-manager/src/controller.ts b/packages/request-manager/src/controller.ts index ec1314354fd..ade8cdd97af 100644 --- a/packages/request-manager/src/controller.ts +++ b/packages/request-manager/src/controller.ts @@ -2,7 +2,6 @@ import { EventEmitter } from 'events'; import path from 'path'; import { ScheduleActionParams, ScheduledAction, scheduleAction } from '@trezor/utils'; -import { checkFileExists } from '@trezor/node-utils'; import { TorControlPort } from './torControlPort'; import { @@ -85,13 +84,9 @@ export class TorController extends EventEmitter { }); } - public async getTorConfiguration( - processId: number, - snowflakeBinaryPath?: string, - ): Promise { + public getTorConfiguration(processId: number): string[] { const { torDataDir } = this.options; const controlAuthCookiePath = path.join(torDataDir, 'control_auth_cookie'); - const snowflakeLogPath = path.join(torDataDir, 'snowflake.log'); // https://github.com/torproject/tor/blob/bf30943cb75911d70367106af644d4273baaa85d/doc/man/tor.1.txt const config: string[] = [ @@ -154,40 +149,6 @@ export class TorController extends EventEmitter { this.options.torDataDir, ]; - let existsSnowflakeBinary = false; - if (snowflakeBinaryPath && snowflakeBinaryPath.trim() !== '') { - // If provided snowflake file does not exists, do not use it. - existsSnowflakeBinary = await checkFileExists(snowflakeBinaryPath); - } - - if (existsSnowflakeBinary) { - // Snowflake is a WebRTC pluggable transport for Tor (client) - // More info: - // https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/tree/main/client - // https://packages.debian.org/bookworm/snowflake-client - - const SNOWFLAKE_PLUGIN = 'snowflake exec'; - const SNOWFLAKE_SERVER = 'snowflake 192.0.2.3:80'; - const SNOWFLAKE_FINGERPRINT = '2B280B23E1107BB62ABFC40DDCC8824814F80A72'; - const SNOWFLAKE_URL = 'https://snowflake-broker.torproject.net.global.prod.fastly.net/'; - const SNOWFLAKE_FRONT = 'fronts=foursquare.com,github.githubassets.com'; - const SNOWFLAKE_ICE = - 'ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478'; - const SNOWFLAKE_UTLS = 'utls-imitate=hellorandomizedalpn'; - - const snowflakeCommand = `${SNOWFLAKE_PLUGIN} ${snowflakeBinaryPath} -log ${snowflakeLogPath}`; - const snowflakeBridge = `${SNOWFLAKE_SERVER} ${SNOWFLAKE_FINGERPRINT} fingerprint=${SNOWFLAKE_FINGERPRINT} url=${SNOWFLAKE_URL} ${SNOWFLAKE_FRONT} ${SNOWFLAKE_ICE} ${SNOWFLAKE_UTLS}`; - - config.push( - '--UseBridges', - '1', - '--ClientTransportPlugin', - snowflakeCommand, - '--Bridge', - snowflakeBridge, - ); - } - return config; } diff --git a/packages/request-manager/src/types.ts b/packages/request-manager/src/types.ts index c567100c445..052a84251d8 100644 --- a/packages/request-manager/src/types.ts +++ b/packages/request-manager/src/types.ts @@ -3,7 +3,6 @@ export interface TorConnectionOptions { port: number; controlPort: number; torDataDir: string; - snowflakeBinaryPath: string; } export interface TorExternalConnectionOptions { From 1f0680b7c44fb2387c4031c499174b50dd1e402b Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 09:00:04 +0100 Subject: [PATCH 08/16] chore(urls): remove snowflake urls --- packages/urls/src/urls.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/urls/src/urls.ts b/packages/urls/src/urls.ts index 5aad9f142a2..ecf0998b97f 100644 --- a/packages/urls/src/urls.ts +++ b/packages/urls/src/urls.ts @@ -128,8 +128,6 @@ export const CHROME_UPDATE_URL: Url = 'https://support.google.com/chrome/answer/ export const CHROME_ANDROID_URL: Url = 'https://play.google.com/store/apps/details?id=com.android.chrome'; export const TOR_PROJECT_URL: Url = 'https://www.torproject.org/'; -export const TOR_SNOWFLAKE_PROJECT_URL: Url = 'https://snowflake.torproject.org/'; -export const TOR_SNOWFLAKE_KB_URL: Url = 'https://trezor.io/learn/a/tor-snowflake-in-trezor-suite'; export const EXPERIMENTAL_FEATURES_KB_URL: Url = 'https://trezor.io/learn/a/experimental-features-in-trezor-suite'; export const EXPERIMENTAL_PASSWORD_MANAGER_KB_URL: Url = From dc554560635d9c0b350faacc888a5a7ae1fe5a4d Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 09:00:36 +0100 Subject: [PATCH 09/16] chore(suite-desktop-core): remove snowflake --- packages/suite-desktop-core/src/index.d.ts | 1 - .../src/libs/processes/TorExternalProcess.ts | 4 ---- .../src/libs/processes/TorProcess.ts | 12 +----------- packages/suite-desktop-core/src/libs/store.ts | 1 - packages/suite-desktop-core/src/modules/tor.ts | 13 ++----------- 5 files changed, 3 insertions(+), 28 deletions(-) diff --git a/packages/suite-desktop-core/src/index.d.ts b/packages/suite-desktop-core/src/index.d.ts index 64d3b1b6a4a..3ea4b0fb9a4 100644 --- a/packages/suite-desktop-core/src/index.d.ts +++ b/packages/suite-desktop-core/src/index.d.ts @@ -103,7 +103,6 @@ declare type TorSettings = { port: number; // Port of the Tor process through which traffic is routed controlPort: number; // Port of the Tor Control Port torDataDir: string; // Path of tor data directory - snowflakeBinaryPath: string; // Path in user system to the snowflake binary useExternalTor: boolean; // Tor should use external daemon instead of the one built-in suite. }; diff --git a/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts b/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts index 76d8fb152ae..a2e30fe459e 100644 --- a/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts +++ b/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts @@ -16,10 +16,6 @@ export class TorExternalProcess { this.torController = new TorControllerExternal({ host: this.host, port: this.port }); } - public setTorConfig(_torConfig: { useExternalTor: boolean; snowflakeBinaryPath: string }) { - // Do nothing - } - public getPort() { return this.port; } diff --git a/packages/suite-desktop-core/src/libs/processes/TorProcess.ts b/packages/suite-desktop-core/src/libs/processes/TorProcess.ts index 97b7183f5a8..2a543a7c5f8 100644 --- a/packages/suite-desktop-core/src/libs/processes/TorProcess.ts +++ b/packages/suite-desktop-core/src/libs/processes/TorProcess.ts @@ -11,7 +11,6 @@ export class TorProcess extends BaseProcess { controlPort: number; torHost: string; torDataDir: string; - snowflakeBinaryPath: string; constructor(options: TorConnectionOptions) { super('tor', 'tor'); @@ -20,21 +19,15 @@ export class TorProcess extends BaseProcess { this.controlPort = options.controlPort; this.torHost = options.host; this.torDataDir = options.torDataDir; - this.snowflakeBinaryPath = ''; this.torController = new TorController({ host: this.torHost, port: this.port, controlPort: this.controlPort, torDataDir: this.torDataDir, - snowflakeBinaryPath: this.snowflakeBinaryPath, }); } - public setTorConfig(torConfig: Pick) { - this.snowflakeBinaryPath = torConfig.snowflakeBinaryPath; - } - public getPort() { return this.port; } @@ -51,10 +44,7 @@ export class TorProcess extends BaseProcess { public async start(): Promise { const electronProcessId = process.pid; - const torConfiguration = await this.torController.getTorConfiguration( - electronProcessId, - this.snowflakeBinaryPath, - ); + const torConfiguration = this.torController.getTorConfiguration(electronProcessId); await super.start(torConfiguration); diff --git a/packages/suite-desktop-core/src/libs/store.ts b/packages/suite-desktop-core/src/libs/store.ts index 67700c2b90f..42cd31a25af 100644 --- a/packages/suite-desktop-core/src/libs/store.ts +++ b/packages/suite-desktop-core/src/libs/store.ts @@ -65,7 +65,6 @@ export class Store { port: 9050, controlPort: 9051, host: '127.0.0.1', - snowflakeBinaryPath: '', useExternalTor: false, torDataDir: '', }); diff --git a/packages/suite-desktop-core/src/modules/tor.ts b/packages/suite-desktop-core/src/modules/tor.ts index cde6a58003b..f4d1ce2773b 100644 --- a/packages/suite-desktop-core/src/modules/tor.ts +++ b/packages/suite-desktop-core/src/modules/tor.ts @@ -38,7 +38,6 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) port: settings.port, controlPort: settings.controlPort, torDataDir: settings.torDataDir, - snowflakeBinaryPath: settings.snowflakeBinaryPath, }), }, { @@ -145,7 +144,7 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) }; const setupTor = async (shouldEnableTor: boolean) => { - const { useExternalTor, snowflakeBinaryPath } = store.getTorSettings(); + const { useExternalTor } = store.getTorSettings(); const isTorRunning = (await getTarget().status()).process; @@ -161,7 +160,6 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) getTarget().torController.on('bootstrap/event', handleBootstrapEvent); try { - getTarget().setTorConfig({ snowflakeBinaryPath, useExternalTor }); updateTorPort(port); if (useExternalTor) { await getTarget().start(); @@ -196,19 +194,12 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) ipcMain.handle( 'tor/change-settings', - ( - ipcEvent, - { - snowflakeBinaryPath, - useExternalTor, - }: { snowflakeBinaryPath: string; useExternalTor: boolean }, - ) => { + (ipcEvent, { useExternalTor }: { useExternalTor: boolean }) => { validateIpcMessage(ipcEvent); try { store.setTorSettings({ ...store.getTorSettings(), - snowflakeBinaryPath, useExternalTor, }); From e355695668d2cd11ef8a810ca4aa435d0c70efdd Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 09:35:32 +0100 Subject: [PATCH 10/16] chore(suite-desktop-api): remove snwoflake --- packages/suite-desktop-api/src/factory.ts | 2 +- packages/suite-desktop-api/src/messages.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/suite-desktop-api/src/factory.ts b/packages/suite-desktop-api/src/factory.ts index 17269e44fa1..baa14bfd23f 100644 --- a/packages/suite-desktop-api/src/factory.ts +++ b/packages/suite-desktop-api/src/factory.ts @@ -125,7 +125,7 @@ export const factory = >( getTorSettings: () => ipcRenderer.invoke('tor/get-settings'), changeTorSettings: payload => { - if (validation.isObject({ snowflakeBinaryPath: 'string' }, payload)) { + if (validation.isObject({ useExternalTor: 'boolean' }, payload)) { return ipcRenderer.invoke('tor/change-settings', payload); } diff --git a/packages/suite-desktop-api/src/messages.ts b/packages/suite-desktop-api/src/messages.ts index 24f0cc42059..b8e5bc6cf2e 100644 --- a/packages/suite-desktop-api/src/messages.ts +++ b/packages/suite-desktop-api/src/messages.ts @@ -54,7 +54,6 @@ export type HandshakeTorModule = { }; export type TorSettings = { - snowflakeBinaryPath: string; useExternalTor: boolean; }; From d466407531c08a9b06c263e8f10c904e0db48420 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 09:36:07 +0100 Subject: [PATCH 11/16] feat(suite): remove snowflakes experimental feature --- .../suite/src/constants/suite/experimental.ts | 63 ++++----- packages/suite/src/support/messages.ts | 30 ----- packages/suite/src/types/suite/index.ts | 5 - .../SettingsGeneral/SettingsGeneral.tsx | 6 - .../settings/SettingsGeneral/TorSnowflake.tsx | 125 ------------------ 5 files changed, 28 insertions(+), 201 deletions(-) delete mode 100644 packages/suite/src/views/settings/SettingsGeneral/TorSnowflake.tsx diff --git a/packages/suite/src/constants/suite/experimental.ts b/packages/suite/src/constants/suite/experimental.ts index 1c96d8b7fd8..44adfa986ec 100644 --- a/packages/suite/src/constants/suite/experimental.ts +++ b/packages/suite/src/constants/suite/experimental.ts @@ -1,12 +1,11 @@ import { TranslationKey } from '@suite-common/intl-types'; import { desktopApi } from '@trezor/suite-desktop-api'; -import { EXPERIMENTAL_PASSWORD_MANAGER_KB_URL, TOR_SNOWFLAKE_KB_URL, Url } from '@trezor/urls'; +import { EXPERIMENTAL_PASSWORD_MANAGER_KB_URL, Url } from '@trezor/urls'; +import { isDesktop } from '@trezor/env-utils'; import { Route } from '@suite-common/suite-types'; import { Dispatch } from '../../types/suite'; -export type ExperimentalFeature = 'password-manager' | 'tor-snowflake' | 'tor-external'; - export type ExperimentalFeatureConfig = { title: TranslationKey; description: TranslationKey; @@ -16,42 +15,36 @@ export type ExperimentalFeatureConfig = { onToggle?: ({ newValue, dispatch }: { newValue: boolean; dispatch: Dispatch }) => void; }; -export const EXPERIMENTAL_FEATURES: Record = { +export type ExperimentalFeaturesConfig = { + 'password-manager': ExperimentalFeatureConfig; + // Tor related experimental feature is optional since it should be only when desktop. + 'tor-external'?: ExperimentalFeatureConfig; +}; + +export const EXPERIMENTAL_FEATURES: ExperimentalFeaturesConfig = { 'password-manager': { title: 'TR_EXPERIMENTAL_PASSWORD_MANAGER', description: 'TR_EXPERIMENTAL_PASSWORD_MANAGER_DESCRIPTION', knowledgeBaseUrl: EXPERIMENTAL_PASSWORD_MANAGER_KB_URL, routeName: 'password-manager-index', }, - 'tor-snowflake': { - title: 'TR_EXPERIMENTAL_TOR_SNOWFLAKE', - description: 'TR_EXPERIMENTAL_TOR_SNOWFLAKE_DESCRIPTION', - knowledgeBaseUrl: TOR_SNOWFLAKE_KB_URL, - onToggle: async ({ newValue }) => { - if (!newValue) { - const result = await desktopApi.getTorSettings(); - if (result.success && result.payload.snowflakeBinaryPath !== '') { - await desktopApi.changeTorSettings({ - ...result.payload, - snowflakeBinaryPath: '', - }); - } - } - }, - }, - 'tor-external': { - title: 'TR_EXPERIMENTAL_TOR_EXTERNAL', - description: 'TR_EXPERIMENTAL_TOR_EXTERNAL_DESCRIPTION', - // TODO: create knowledge base page for this! - // knowledgeBaseUrl: TOR_EXTERNAL_KNOWLEDGE_BASE, - onToggle: async ({ newValue }) => { - const result = await desktopApi.getTorSettings(); - if (result.success && result.payload.useExternalTor !== newValue) { - await desktopApi.changeTorSettings({ - ...result.payload, - useExternalTor: newValue, - }); - } - }, - }, + ...(isDesktop() + ? { + 'tor-external': { + title: 'TR_EXPERIMENTAL_TOR_EXTERNAL', + description: 'TR_EXPERIMENTAL_TOR_EXTERNAL_DESCRIPTION', + // TODO: create knowledge base page for this! + // knowledgeBaseUrl: TOR_EXTERNAL_KNOWLEDGE_BASE, + onToggle: async ({ newValue }) => { + const result = await desktopApi.getTorSettings(); + if (result.success && result.payload.useExternalTor !== newValue) { + await desktopApi.changeTorSettings({ + ...result.payload, + useExternalTor: newValue, + }); + } + }, + }, + } + : {}), }; diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index ea3da7d84be..d92d06b0df7 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -4052,27 +4052,6 @@ export default defineMessages({ id: 'TR_ONION_LINKS_TITLE', defaultMessage: 'Open trezor.io links as .onion links', }, - TR_TOR_CONFIG_SNOWFLAKE_TITLE: { - id: 'TR_TOR_CONFIG_SNOWFLAKE_TITLE', - defaultMessage: 'Tor Snowflake Binary Path', - }, - TR_TOR_CONFIG_SNOWFLAKE_DESCRIPTION: { - id: 'TR_TOR_CONFIG_SNOWFLAKE_DESCRIPTION', - defaultMessage: - 'Enter the path to the Tor Snowflake binary on your system. Make sure Tor is disabled before making this change.', - }, - TR_TOR_CONFIG_SNOWFLAKE_ERROR_PATH: { - id: 'TR_TOR_CONFIG_SNOWFLAKE_ERROR_PATH', - defaultMessage: 'Must be a valid full path.', - }, - TR_TOR_CONFIG_SNOWFLAKE_UPDATE_LABEL: { - id: 'TR_TOR_CONFIG_SNOWFLAKE_UPDATE_LABEL', - defaultMessage: 'Update path', - }, - TR_TOR_CONFIG_SNOWFLAKE_DISABLE_LABEL: { - id: 'TR_TOR_CONFIG_SNOWFLAKE_DISABLE_LABEL', - defaultMessage: 'Disable Tor Snowflake', - }, TR_TOR_ENABLE_TITLE: { id: 'TR_TOR_ENABLE_TITLE', defaultMessage: 'Enable Tor', @@ -5001,15 +4980,6 @@ export default defineMessages({ defaultMessage: 'Use this utility to retrieve passwords stored on Dropbox and secured by Trezor. Designed for former users of the Trezor Password Manager Chrome extension.', }, - TR_EXPERIMENTAL_TOR_SNOWFLAKE: { - id: 'TR_EXPERIMENTAL_TOR_SNOWFLAKE', - defaultMessage: 'Tor Snowflake', - }, - TR_EXPERIMENTAL_TOR_SNOWFLAKE_DESCRIPTION: { - id: 'TR_EXPERIMENTAL_TOR_SNOWFLAKE_DESCRIPTION', - defaultMessage: - 'Access censored websites and apps using Tor Snowflake, a system designed to bypass restrictions.', - }, TR_EXPERIMENTAL_TOR_EXTERNAL: { id: 'TR_EXPERIMENTAL_TOR_EXTERNAL', defaultMessage: 'Tor external', diff --git a/packages/suite/src/types/suite/index.ts b/packages/suite/src/types/suite/index.ts index b8a8cc4e233..d5739278aec 100644 --- a/packages/suite/src/types/suite/index.ts +++ b/packages/suite/src/types/suite/index.ts @@ -117,11 +117,6 @@ export interface TorBootstrap { isSlow?: boolean; } -export type TorConfig = { - enableSnowflake: boolean; - snowflakeBinaryPath: string; -}; - export enum DisplayMode { CHUNKS = 1, PAGINATED_TEXT, diff --git a/packages/suite/src/views/settings/SettingsGeneral/SettingsGeneral.tsx b/packages/suite/src/views/settings/SettingsGeneral/SettingsGeneral.tsx index d13a60e79ed..2972d683f3f 100644 --- a/packages/suite/src/views/settings/SettingsGeneral/SettingsGeneral.tsx +++ b/packages/suite/src/views/settings/SettingsGeneral/SettingsGeneral.tsx @@ -5,7 +5,6 @@ import { SettingsLayout, SettingsSection } from 'src/components/settings'; import { Translation } from 'src/components/suite'; import { useLayoutSize, useSelector } from 'src/hooks/suite'; import { - selectHasExperimentalFeature, selectIsSettingsDesktopAppPromoBannerShown, selectTorState, } from 'src/reducers/suite/suiteReducer'; @@ -30,7 +29,6 @@ import { DesktopSuiteBanner } from './DesktopSuiteBanner'; import { AddressDisplay } from './AddressDisplay'; import { EnableViewOnly } from './EnableViewOnly'; import { Experimental } from './Experimental'; -import { TorSnowflake } from './TorSnowflake'; import { AutomaticUpdate } from './AutomaticUpdate'; import { AutoStart } from './AutoStart'; import { ShowOnTray } from './ShowOnTray'; @@ -45,9 +43,6 @@ export const SettingsGeneral = () => { const desktopUpdate = useSelector(state => state.desktopUpdate); const metadata = useSelector(state => state.metadata); const { isMobileLayout } = useLayoutSize(); - const torSnowflakeExperimentalFeature = useSelector( - selectHasExperimentalFeature('tor-snowflake'), - ); const hasBitcoinNetworks = enabledNetworks.some(symbol => { const networkFeatures = getNetwork(symbol).features; @@ -85,7 +80,6 @@ export const SettingsGeneral = () => { } icon="torBrowser"> {isDesktop() && } {isTorEnabled && } - {isDesktop() && torSnowflakeExperimentalFeature && } )} diff --git a/packages/suite/src/views/settings/SettingsGeneral/TorSnowflake.tsx b/packages/suite/src/views/settings/SettingsGeneral/TorSnowflake.tsx deleted file mode 100644 index acb15cff5dd..00000000000 --- a/packages/suite/src/views/settings/SettingsGeneral/TorSnowflake.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { ChangeEventHandler, useEffect, useState } from 'react'; - -import styled from 'styled-components'; - -import { TorSettings } from '@trezor/suite-desktop-api/src/messages'; -import { TOR_SNOWFLAKE_KB_URL } from '@trezor/urls'; -import { breakpointMediaQueries } from '@trezor/styles'; -import { desktopApi } from '@trezor/suite-desktop-api'; -import { Button, Input } from '@trezor/components'; -import { isFullPath } from '@trezor/utils'; -import { spacingsPx } from '@trezor/theme'; - -import { selectTorState } from 'src/reducers/suite/suiteReducer'; -import { useSelector, useTranslation } from 'src/hooks/suite'; -import { ActionColumn, SectionItem, TextColumn, Translation } from 'src/components/suite'; - -const Container = styled.div` - display: flex; - flex-direction: column; - align-items: center; - gap: ${spacingsPx.sm}; - min-width: 200px; - - ${breakpointMediaQueries.below_sm} { - min-width: 100%; - } -`; - -export const TorSnowflake = () => { - const { isTorEnabled } = useSelector(selectTorState); - const [torSettings, setTorSettings] = useState(null); - const [hasPathChanged, setHasPathChanged] = useState(false); - const [error, setError] = useState(null); - const { translationString } = useTranslation(); - - useEffect(() => { - const fetchTorSettings = async () => { - const result = await desktopApi.getTorSettings(); - if (result.success) { - setTorSettings(result.payload); - } else { - setError(result.error); - } - }; - - fetchTorSettings(); - - const handleTorSettingsChange = (settings: TorSettings) => setTorSettings(settings); - desktopApi.on('tor/settings', handleTorSettingsChange); - - return () => { - desktopApi.removeAllListeners('tor/settings'); - }; - }, []); - - const handleChange: ChangeEventHandler = ({ target: { value } }) => { - if (!torSettings) return; - - setHasPathChanged(true); - if (!isFullPath(value) && value !== '') { - setError(translationString('TR_TOR_CONFIG_SNOWFLAKE_ERROR_PATH')); - } else { - setError(null); - } - setTorSettings(prevSettings => ({ - ...prevSettings!, - snowflakeBinaryPath: value, - })); - }; - - const handleClick = async () => { - if (!torSettings || error) return; - - await desktopApi.changeTorSettings({ - ...torSettings, - snowflakeBinaryPath: torSettings.snowflakeBinaryPath, - }); - setHasPathChanged(false); - }; - - const isUpdateDisabled = - !torSettings || - !!error || - (!isFullPath(torSettings.snowflakeBinaryPath) && torSettings.snowflakeBinaryPath !== '') || - isTorEnabled || - !hasPathChanged; - - if (!torSettings) return null; - - const buttonTranslationId = - torSettings.snowflakeBinaryPath === '' && hasPathChanged - ? 'TR_TOR_CONFIG_SNOWFLAKE_DISABLE_LABEL' - : 'TR_TOR_CONFIG_SNOWFLAKE_UPDATE_LABEL'; - - return ( - - } - description={} - buttonLink={TOR_SNOWFLAKE_KB_URL} - /> - - - - - - - - ); -}; From 02eaaa596dde35613825cf8f6874e12f11f05815 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 10:12:23 +0100 Subject: [PATCH 12/16] fixup! feat(suite): remove snowflakes experimental feature --- .../suite/src/constants/suite/experimental.ts | 48 ++++++++----------- .../settings/SettingsGeneral/Experimental.tsx | 19 ++++++-- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/packages/suite/src/constants/suite/experimental.ts b/packages/suite/src/constants/suite/experimental.ts index 44adfa986ec..c6847e6489b 100644 --- a/packages/suite/src/constants/suite/experimental.ts +++ b/packages/suite/src/constants/suite/experimental.ts @@ -1,50 +1,44 @@ import { TranslationKey } from '@suite-common/intl-types'; import { desktopApi } from '@trezor/suite-desktop-api'; import { EXPERIMENTAL_PASSWORD_MANAGER_KB_URL, Url } from '@trezor/urls'; -import { isDesktop } from '@trezor/env-utils'; import { Route } from '@suite-common/suite-types'; import { Dispatch } from '../../types/suite'; +export type ExperimentalFeature = 'password-manager' | 'tor-external'; + export type ExperimentalFeatureConfig = { title: TranslationKey; description: TranslationKey; + isDesktopOnly: boolean; knowledgeBaseUrl?: Url; routeName?: Route['name']; isDisabled?: (context: { isDebug: boolean }) => boolean; onToggle?: ({ newValue, dispatch }: { newValue: boolean; dispatch: Dispatch }) => void; }; -export type ExperimentalFeaturesConfig = { - 'password-manager': ExperimentalFeatureConfig; - // Tor related experimental feature is optional since it should be only when desktop. - 'tor-external'?: ExperimentalFeatureConfig; -}; - -export const EXPERIMENTAL_FEATURES: ExperimentalFeaturesConfig = { +export const EXPERIMENTAL_FEATURES: Record = { 'password-manager': { title: 'TR_EXPERIMENTAL_PASSWORD_MANAGER', description: 'TR_EXPERIMENTAL_PASSWORD_MANAGER_DESCRIPTION', knowledgeBaseUrl: EXPERIMENTAL_PASSWORD_MANAGER_KB_URL, routeName: 'password-manager-index', + isDesktopOnly: false, + }, + 'tor-external': { + title: 'TR_EXPERIMENTAL_TOR_EXTERNAL', + description: 'TR_EXPERIMENTAL_TOR_EXTERNAL_DESCRIPTION', + // TODO: create knowledge base page for this! + // knowledgeBaseUrl: TOR_EXTERNAL_KNOWLEDGE_BASE, + isDesktopOnly: true, + onToggle: async ({ newValue }) => { + const result = await desktopApi.getTorSettings(); + if (result.success && result.payload.useExternalTor !== newValue) { + await desktopApi.changeTorSettings({ + ...result.payload, + useExternalTor: newValue, + }); + } + }, }, - ...(isDesktop() - ? { - 'tor-external': { - title: 'TR_EXPERIMENTAL_TOR_EXTERNAL', - description: 'TR_EXPERIMENTAL_TOR_EXTERNAL_DESCRIPTION', - // TODO: create knowledge base page for this! - // knowledgeBaseUrl: TOR_EXTERNAL_KNOWLEDGE_BASE, - onToggle: async ({ newValue }) => { - const result = await desktopApi.getTorSettings(); - if (result.success && result.payload.useExternalTor !== newValue) { - await desktopApi.changeTorSettings({ - ...result.payload, - useExternalTor: newValue, - }); - } - }, - }, - } - : {}), }; diff --git a/packages/suite/src/views/settings/SettingsGeneral/Experimental.tsx b/packages/suite/src/views/settings/SettingsGeneral/Experimental.tsx index e523d6f0a99..678535ed9d6 100644 --- a/packages/suite/src/views/settings/SettingsGeneral/Experimental.tsx +++ b/packages/suite/src/views/settings/SettingsGeneral/Experimental.tsx @@ -4,6 +4,7 @@ import { AnimatePresence, motion } from 'framer-motion'; import { Checkbox, Switch, Banner, Button } from '@trezor/components'; import { spacingsPx } from '@trezor/theme'; import { EXPERIMENTAL_FEATURES_KB_URL } from '@trezor/urls'; +import { isDesktop } from '@trezor/env-utils'; import { SUITE } from 'src/actions/suite/constants'; import { ActionColumn, SectionItem, TextColumn, Translation } from 'src/components/suite'; @@ -109,10 +110,20 @@ export const Experimental = () => { }); }; - const experimentalFeatures = Object.keys(EXPERIMENTAL_FEATURES).filter( - feature => - !EXPERIMENTAL_FEATURES[feature as ExperimentalFeature]?.isDisabled?.({ isDebug }), - ); + const experimentalFeatures = Object.keys(EXPERIMENTAL_FEATURES) + .filter( + feature => + !EXPERIMENTAL_FEATURES[feature as ExperimentalFeature]?.isDisabled?.({ isDebug }), + ) + .filter( + feature => + !( + EXPERIMENTAL_FEATURES[feature as ExperimentalFeature].isDesktopOnly && + !isDesktop() + ), + ); + + console.log('experimentalFeatures', experimentalFeatures); return ( <> From 72259fa752c43dcde3b014f9af8cca407ea8ec51 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 15:31:03 +0100 Subject: [PATCH 13/16] fixup! feat(suite): remove snowflakes experimental feature --- .../suite/src/constants/suite/experimental.ts | 5 ++--- .../settings/SettingsGeneral/Experimental.tsx | 19 ++++--------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/suite/src/constants/suite/experimental.ts b/packages/suite/src/constants/suite/experimental.ts index c6847e6489b..a3382a76fc3 100644 --- a/packages/suite/src/constants/suite/experimental.ts +++ b/packages/suite/src/constants/suite/experimental.ts @@ -2,6 +2,7 @@ import { TranslationKey } from '@suite-common/intl-types'; import { desktopApi } from '@trezor/suite-desktop-api'; import { EXPERIMENTAL_PASSWORD_MANAGER_KB_URL, Url } from '@trezor/urls'; import { Route } from '@suite-common/suite-types'; +import { isDesktop } from '@trezor/env-utils'; import { Dispatch } from '../../types/suite'; @@ -10,7 +11,6 @@ export type ExperimentalFeature = 'password-manager' | 'tor-external'; export type ExperimentalFeatureConfig = { title: TranslationKey; description: TranslationKey; - isDesktopOnly: boolean; knowledgeBaseUrl?: Url; routeName?: Route['name']; isDisabled?: (context: { isDebug: boolean }) => boolean; @@ -23,14 +23,13 @@ export const EXPERIMENTAL_FEATURES: Record { const result = await desktopApi.getTorSettings(); if (result.success && result.payload.useExternalTor !== newValue) { diff --git a/packages/suite/src/views/settings/SettingsGeneral/Experimental.tsx b/packages/suite/src/views/settings/SettingsGeneral/Experimental.tsx index 678535ed9d6..e523d6f0a99 100644 --- a/packages/suite/src/views/settings/SettingsGeneral/Experimental.tsx +++ b/packages/suite/src/views/settings/SettingsGeneral/Experimental.tsx @@ -4,7 +4,6 @@ import { AnimatePresence, motion } from 'framer-motion'; import { Checkbox, Switch, Banner, Button } from '@trezor/components'; import { spacingsPx } from '@trezor/theme'; import { EXPERIMENTAL_FEATURES_KB_URL } from '@trezor/urls'; -import { isDesktop } from '@trezor/env-utils'; import { SUITE } from 'src/actions/suite/constants'; import { ActionColumn, SectionItem, TextColumn, Translation } from 'src/components/suite'; @@ -110,20 +109,10 @@ export const Experimental = () => { }); }; - const experimentalFeatures = Object.keys(EXPERIMENTAL_FEATURES) - .filter( - feature => - !EXPERIMENTAL_FEATURES[feature as ExperimentalFeature]?.isDisabled?.({ isDebug }), - ) - .filter( - feature => - !( - EXPERIMENTAL_FEATURES[feature as ExperimentalFeature].isDesktopOnly && - !isDesktop() - ), - ); - - console.log('experimentalFeatures', experimentalFeatures); + const experimentalFeatures = Object.keys(EXPERIMENTAL_FEATURES).filter( + feature => + !EXPERIMENTAL_FEATURES[feature as ExperimentalFeature]?.isDisabled?.({ isDebug }), + ); return ( <> From 24fd8769e17b2c72ba4656ee57f3faa9bda482cd Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 15:38:45 +0100 Subject: [PATCH 14/16] fixup! chore(request-manager): remove snowflake direct support --- packages/request-manager/e2e/identities-stress.ts | 2 +- packages/request-manager/e2e/interceptor.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/request-manager/e2e/identities-stress.ts b/packages/request-manager/e2e/identities-stress.ts index ca449f5bb3d..faad13c90c0 100644 --- a/packages/request-manager/e2e/identities-stress.ts +++ b/packages/request-manager/e2e/identities-stress.ts @@ -38,7 +38,7 @@ const intervalBetweenRequests = 1000 * 20; controlPort, torDataDir, }); - const torParams = await torController.getTorConfiguration(processId); + const torParams = torController.getTorConfiguration(processId); // Starting Tor process from binary. torRunner({ torParams, diff --git a/packages/request-manager/e2e/interceptor.test.ts b/packages/request-manager/e2e/interceptor.test.ts index c47747ef58c..5186a783a83 100644 --- a/packages/request-manager/e2e/interceptor.test.ts +++ b/packages/request-manager/e2e/interceptor.test.ts @@ -45,7 +45,7 @@ describe('Interceptor', () => { controlPort, torDataDir, }); - const torParams = await torController.getTorConfiguration(processId); + const torParams = torController.getTorConfiguration(processId); // Starting Tor process from binary. torProcess = torRunner({ torParams, From 1d3da5aa4c9aad5d21435018b6104ca50dc27e8b Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 16:08:29 +0100 Subject: [PATCH 15/16] fixup! chore(suite-desktop-core): remove snowflake --- .../suite-desktop-core/src/modules/tor.ts | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/packages/suite-desktop-core/src/modules/tor.ts b/packages/suite-desktop-core/src/modules/tor.ts index f4d1ce2773b..3d2af751064 100644 --- a/packages/suite-desktop-core/src/modules/tor.ts +++ b/packages/suite-desktop-core/src/modules/tor.ts @@ -30,27 +30,23 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) const settings = store.getTorSettings(); - const processes = [ - { - type: 'tor', - process: new TorProcess({ - host: settings.host, - port: settings.port, - controlPort: settings.controlPort, - torDataDir: settings.torDataDir, - }), - }, - { - type: 'tor-external', - process: new TorExternalProcess(), - }, - ]; + const bundledTorProcess = new TorProcess({ + host: settings.host, + port: settings.port, + controlPort: settings.controlPort, + torDataDir: settings.torDataDir, + }); + + const externalTorProcess = new TorExternalProcess(); const getTarget = () => { const { useExternalTor } = store.getTorSettings(); - const currentTarget = useExternalTor ? 'tor-external' : 'tor'; - return processes.find(process => process.type === currentTarget)!.process; + if (useExternalTor) { + return externalTorProcess; + } + + return bundledTorProcess; }; const updateTorPort = (port: number) => { From 3f2455b3044907f9d16454071cdbe5a0d5593bc6 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Fri, 13 Dec 2024 16:09:31 +0100 Subject: [PATCH 16/16] fixup! feat(suite): remove snowflakes experimental feature --- packages/suite/src/constants/suite/experimental.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/suite/src/constants/suite/experimental.ts b/packages/suite/src/constants/suite/experimental.ts index a3382a76fc3..adbd887b33d 100644 --- a/packages/suite/src/constants/suite/experimental.ts +++ b/packages/suite/src/constants/suite/experimental.ts @@ -29,7 +29,7 @@ export const EXPERIMENTAL_FEATURES: Record !isDesktop(), onToggle: async ({ newValue }) => { const result = await desktopApi.getTorSettings(); if (result.success && result.payload.useExternalTor !== newValue) {