From c745690ebed9e50db6507a3123294529ff27ee16 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 23 May 2024 10:56:52 -0400 Subject: [PATCH] Tor improvements and better test fix --- .../connections-manager.service.tor.spec.ts | 10 ++++++-- .../backend/src/nest/ipfs/ipfs.service.ts | 14 +++++++---- .../backend/src/nest/libp2p/libp2p.service.ts | 24 +++++++++++++++---- packages/backend/src/nest/tor/tor.service.ts | 20 ++++++++++++---- packages/mobile/ios/TorHandler.swift | 6 +++++ 5 files changed, 60 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts index 4506ed02d8..633d111339 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts @@ -208,13 +208,19 @@ describe('Connections manager', () => { hiddenService: userIdentity.hiddenService, }, } + const emitSpy = jest.spyOn(libp2pService, 'emit') await connectionsManagerService.init() await connectionsManagerService.launchCommunity(launchCommunityPayload) - await sleep(5000) + await waitForExpect(async () => { + expect(emitSpy).toHaveBeenCalledWith(Libp2pEvents.INITIAL_DIAL) + }, 30000) + // It looks LibP2P dials peers initially when it's started and // then IPFS service dials peers again when started, thus // peersCount-1 * 2 because we don't dial ourself (the first peer in the list) - expect(spyOnDial).toHaveBeenCalledTimes((peersCount - 1) * 2) + await waitForExpect(async () => { + expect(spyOnDial).toHaveBeenCalledTimes((peersCount - 1) * 2) + }, 45000) // Temporary fix for hanging test - websocketOverTor doesn't have abortController await sleep(5000) }) diff --git a/packages/backend/src/nest/ipfs/ipfs.service.ts b/packages/backend/src/nest/ipfs/ipfs.service.ts index bd5c4024d9..e27ecd2f92 100644 --- a/packages/backend/src/nest/ipfs/ipfs.service.ts +++ b/packages/backend/src/nest/ipfs/ipfs.service.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common' import { LazyModuleLoader } from '@nestjs/core' -import { create, IPFS } from 'ipfs-core' +import { create, IPFS, Options as IPFSOptions } from 'ipfs-core' import { IPFS_REPO_PATCH } from '../const' import Logger from '../common/logger' +import { Libp2p } from 'libp2p' @Injectable() export class IpfsService { @@ -29,9 +30,12 @@ export class IpfsService { this.logger.error('no libp2p instance') throw new Error('no libp2p instance') } - ipfs = await create({ + const getLibp2p = async (...args: any[]): Promise => { + return libp2pInstance + } + const ipfsConfig: IPFSOptions = { start: false, - libp2p: async () => libp2pInstance, + libp2p: getLibp2p, preload: { enabled: false }, repo: this.ipfsRepoPath, EXPERIMENTAL: { @@ -40,7 +44,9 @@ export class IpfsService { init: { privateKey: peerId, }, - }) + } + this.logger('Creating new ipfs instance') + ipfs = await create(ipfsConfig) this.ipfsInstance = ipfs } catch (error) { this.logger.error('ipfs creation failed', error) diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index 5b552217e6..f2264092be 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -10,7 +10,7 @@ import crypto from 'crypto' import { EventEmitter } from 'events' import { Agent } from 'https' import { createServer } from 'it-ws' -import { Libp2p, createLibp2p } from 'libp2p' +import { Libp2p, createLibp2p, Libp2pOptions } from 'libp2p' import { preSharedKey } from 'libp2p/pnet' import { DateTime } from 'luxon' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' @@ -163,7 +163,9 @@ export class Libp2pService extends EventEmitter { } public async createInstance(params: Libp2pNodeParams, startDialImmediately: boolean = false): Promise { + this.logger(`Creating new libp2p instance`) if (this.libp2pInstance) { + this.logger(`Libp2p instance already exists`) return this.libp2pInstance } @@ -171,13 +173,16 @@ export class Libp2pService extends EventEmitter { const maxParallelDials = 2 try { - libp2p = await createLibp2p({ + const libp2pConfig: Libp2pOptions = { start: false, connectionManager: { minConnections: 5, // TODO: increase? maxConnections: 20, // TODO: increase? - dialTimeout: 120000, + dialTimeout: 90_000, maxParallelDials, + maxIncomingPendingConnections: 30, + inboundConnectionThreshold: 30, + inboundUpgradeTimeout: 45_000, autoDial: true, // It's a default but let's set it to have explicit information }, peerId: params.peerId, @@ -196,6 +201,16 @@ export class Libp2pService extends EventEmitter { active: false, }, }, + // ping: { + // maxInboundStreams: 10, + // maxOutboundStreams: 10, + // timeout: 15_000 + // }, + // fetch: { + // maxInboundStreams: 10, + // maxOutboundStreams: 10, + // timeout: 15_000 + // }, transports: [ webSockets({ filter: all, @@ -212,7 +227,8 @@ export class Libp2pService extends EventEmitter { allowPublishToZeroPeers: true, doPX: true, }), - }) + } + libp2p = await createLibp2p(libp2pConfig) } catch (err) { this.logger.error('Create libp2p:', err) throw err diff --git a/packages/backend/src/nest/tor/tor.service.ts b/packages/backend/src/nest/tor/tor.service.ts index 8bbbbb3208..277f6b3288 100644 --- a/packages/backend/src/nest/tor/tor.service.ts +++ b/packages/backend/src/nest/tor/tor.service.ts @@ -56,13 +56,22 @@ export class Tor extends EventEmitter implements OnModuleInit { this.controlPort = port } - mergeDefaultTorParams = (params: TorParams = {}): TorParams => { - const defaultParams = { + private mergeDefaultTorParams(params: TorParams = {}): TorParams { + const defaultParams: TorParams = { '--NumEntryGuards': '3', // See task #1295 + '--LearnCircuitBuildTimeout': '1', + '--CircuitBuildTimeout': '10', + '--KeepalivePeriod': '15', + '--NewCircuitPeriod': '300', } return { ...defaultParams, ...params } } + private mergeDefaultTorParamsAndFlatten(params: TorParams = {}): string[] { + const mergedParams = this.mergeDefaultTorParams(params) + return Array.from(Object.entries(mergedParams)).flat() + } + get torProcessParams(): string[] { return Array.from(Object.entries(this.extraTorProcessParams)).flat() } @@ -134,11 +143,12 @@ export class Tor extends EventEmitter implements OnModuleInit { this.logger(`Sending ${SocketActionTypes.INITIAL_DIAL}`) this.emit(SocketActionTypes.INITIAL_DIAL) clearInterval(this.interval) - resolve() + clearTimeout(this.initTimeout) } }, 2500) this.logger(`Spawned tor with pid(s): ${this.getTorProcessIds()}`) + resolve() } catch (e) { this.logger('Killing tor due to error', e) this.clearHangingTorProcess() @@ -267,7 +277,9 @@ export class Tor extends EventEmitter implements OnModuleInit { `"${this.torDataDirectory}"`, '--HashedControlPassword', this.torPasswordProvider.torHashedPassword, - // ...this.torProcessParams + '--LongLivedPorts', + `80,${this.configOptions.httpTunnelPort},${this.socksPort}`, + ...this.mergeDefaultTorParamsAndFlatten(), ], options ) diff --git a/packages/mobile/ios/TorHandler.swift b/packages/mobile/ios/TorHandler.swift index 1a62ade0f4..788a78261c 100644 --- a/packages/mobile/ios/TorHandler.swift +++ b/packages/mobile/ios/TorHandler.swift @@ -20,6 +20,12 @@ class TorHandler: NSObject { "--ControlPort", "127.0.0.1:\(controlPort)", "--HTTPTunnelPort", "127.0.0.1:\(httpTunnelPort)", "--Log", log_loc, + "--NumEntryGuards", "3", + "--LearnCircuitBuildTimeout", "1", + "--CircuitBuildTimeout", "10", + "--KeepalivePeriod", "15", + "--NewCircuitPeriod", "300", + "--LongLivedPorts", "80,\(socksPort),\(httpTunnelPort)", ] if let dataDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)