Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add storage server proxy for downloading and uploading data fro… #2393

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3c6796d
feat: add storage server proxy for downloading and uploading data fro…
EmiM Mar 27, 2024
c6c9df4
feat: joining with v2 invitation link
EmiM Mar 28, 2024
6e35699
feat: handle relay server connection errors
EmiM Mar 28, 2024
7e6b4bc
test: add basic storage service proxy tests
EmiM Apr 2, 2024
0963a59
feat: add basic server stored metadata validator
EmiM Apr 2, 2024
fa0d7b9
Merge branch 'develop' into feature/2295
EmiM Apr 4, 2024
376a74c
Merge branch 'feature/2310' into feature/2295
EmiM Apr 4, 2024
2ff58f7
fix: pass dispatch to useEffect
EmiM Apr 4, 2024
7f28330
fix: move handling server error to container
EmiM Apr 5, 2024
3b6ec98
Merge branch 'feature/2310' into feature/2295
EmiM Apr 10, 2024
611ffe5
fix: add missing import
EmiM Apr 10, 2024
521c130
test: add Dockerfile for running QSS with e2e test; add e2e test for …
EmiM Apr 11, 2024
bd07e99
refactor: deduplicate UserTestData interface
EmiM Apr 11, 2024
ed6a907
chore: remove DuplicatedCertBug logs
EmiM Apr 11, 2024
ffaea57
test: add temporary workaround to e2e test to fully join community wi…
EmiM Apr 11, 2024
67b5c27
Better peer sorting and updated initial diallng (#2427)
Apr 12, 2024
f5c7925
fix: add missing file and fix test
EmiM Apr 12, 2024
c18e97c
Remove only (#2429)
Apr 12, 2024
7cc1d8e
Update Quiet client to detect a v2 invite link format (#2330)
EmiM Apr 12, 2024
87ed4ac
Publish
Apr 12, 2024
8801593
Update packages CHANGELOG.md
Apr 12, 2024
eb735f5
Merge branch 'develop' into feature/2295
EmiM Apr 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# New features:

* Add support for new format of invitation link: `c=<cid>&t=<token>&s=<serverAddress>&i=<inviterAddress>` ([#2310](https://github.com/TryQuiet/quiet/issues/2310))
* Use server for downloading initial community metadata if v2 invitation link is detected ([#2295](https://github.com/TryQuiet/quiet/issues/2295))

# Refactorings:

Expand Down
44 changes: 44 additions & 0 deletions packages/backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@
"dotenv": "8.2.0",
"events": "^3.2.0",
"express": "^4.17.1",
"fetch-retry": "^6.0.0",
"fastq": "^1.17.1",
"get-port": "^5.1.1",
"go-ipfs": "npm:[email protected]",
"http-server": "^0.12.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { SocketModule } from '../socket/socket.module'
import { StorageModule } from '../storage/storage.module'
import { TorModule } from '../tor/tor.module'
import { ConnectionsManagerService } from './connections-manager.service'
import { ServerProxyServiceModule } from '../storageServerProxy/storageServerProxy.module'

@Module({
imports: [RegistrationModule, StorageModule, TorModule, SocketModule, LocalDbModule],
imports: [RegistrationModule, StorageModule, TorModule, SocketModule, LocalDbModule, ServerProxyServiceModule],
providers: [ConnectionsManagerService],
exports: [ConnectionsManagerService],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import waitForExpect from 'wait-for-expect'
import { Libp2pEvents } from '../libp2p/libp2p.types'
import { sleep } from '../common/sleep'
import { createLibp2pAddress } from '@quiet/common'
import { lib } from 'crypto-js'

jest.setTimeout(100_000)

Expand Down Expand Up @@ -177,7 +178,6 @@ describe('Connections manager', () => {
const spyOnDestroyHiddenService = jest.spyOn(tor, 'destroyHiddenService')
await connectionsManagerService.init()
const network = await connectionsManagerService.getNetwork()
console.log('network', network)
expect(network.hiddenService.onionAddress.split('.')[0]).toHaveLength(56)
expect(network.hiddenService.privateKey).toHaveLength(99)
const peerId = await PeerId.createFromJSON(network.peerId)
Expand Down Expand Up @@ -216,8 +216,8 @@ describe('Connections manager', () => {
await sleep(5000)
// It looks LibP2P dials peers initially when it's started and
// then IPFS service dials peers again when started, thus
// peersCount * 2
expect(spyOnDial).toHaveBeenCalledTimes(peersCount * 2)
// peersCount-1 * 2 because we don't dial ourself (the first peer in the list)
expect(spyOnDial).toHaveBeenCalledTimes((peersCount - 1) * 2)
// Temporary fix for hanging test - websocketOverTor doesn't have abortController
await sleep(5000)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,69 +1,68 @@
import { peerIdFromKeys } from '@libp2p/peer-id'
import { Inject, Injectable, OnModuleInit } from '@nestjs/common'
import { Crypto } from '@peculiar/webcrypto'
import { Agent } from 'https'
import fs from 'fs'
import path from 'path'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { setEngine, CryptoEngine } from 'pkijs'
import { EventEmitter } from 'events'
import fs from 'fs'
import getPort from 'get-port'
import { Agent } from 'https'
import path from 'path'
import PeerId from 'peer-id'
import { CryptoEngine, setEngine } from 'pkijs'
import { getLibp2pAddressesFromCsrs, removeFilesFromDir } from '../common/utils'

import { LazyModuleLoader } from '@nestjs/core'
import { createLibp2pAddress, isPSKcodeValid, p2pAddressesToPairs } from '@quiet/common'
import { CertFieldsTypes, getCertFieldValue, loadCertificate } from '@quiet/identity'
import {
GetMessagesPayload,
ChannelMessageIdsResponse,
type DeleteChannelResponse,
ChannelSubscribedPayload,
ChannelsReplicatedPayload,
Community,
CommunityId,
CommunityMetadata,
CommunityOwnership,
ConnectionProcessInfo,
CreateChannelPayload,
CreateChannelResponse,
CreateNetworkPayload,
DeleteFilesFromChannelSocketPayload,
DownloadStatus,
ErrorMessages,
FileMetadata,
MessagesLoadedPayload,
GetMessagesPayload,
InitCommunityPayload,
MessagesLoadedPayload,
NetworkDataPayload,
NetworkInfo,
NetworkStats,
type SavedOwnerCertificatePayload,
PushNotificationPayload,
RegisterOwnerCertificatePayload,
RemoveDownloadStatus,
SaveCSRPayload,
SendCertificatesResponse,
SendMessagePayload,
ChannelSubscribedPayload,
SocketActionTypes,
StorePeerListPayload,
UploadFilePayload,
PeerId as PeerIdType,
SaveCSRPayload,
CommunityMetadata,
type PermsData,
type DeleteChannelResponse,
type SavedOwnerCertificatePayload,
type UserProfile,
type UserProfilesStoredEvent,
} from '@quiet/types'
import Logger from '../common/logger'
import { CONFIG_OPTIONS, QUIET_DIR, SERVER_IO_PROVIDER, SOCKS_PROXY_AGENT } from '../const'
import { ConfigOptions, GetPorts, ServerIoProviderTypes } from '../types'
import { SocketService } from '../socket/socket.service'
import { RegistrationService } from '../registration/registration.service'
import { LocalDbService } from '../local-db/local-db.service'
import { StorageService } from '../storage/storage.service'
import { ServiceState, TorInitState } from './connections-manager.types'
import { Libp2pService } from '../libp2p/libp2p.service'
import { Tor } from '../tor/tor.service'
import { LocalDBKeys } from '../local-db/local-db.types'
import { Libp2pEvents, Libp2pNodeParams } from '../libp2p/libp2p.types'
import { LocalDbService } from '../local-db/local-db.service'
import { LocalDBKeys } from '../local-db/local-db.types'
import { RegistrationService } from '../registration/registration.service'
import { RegistrationEvents } from '../registration/registration.types'
import { StorageEvents } from '../storage/storage.types'
import { LazyModuleLoader } from '@nestjs/core'
import Logger from '../common/logger'
import { emitError } from '../socket/socket.errors'
import { createLibp2pAddress, isPSKcodeValid } from '@quiet/common'
import { CertFieldsTypes, createRootCA, getCertFieldValue, loadCertificate } from '@quiet/identity'
import { SocketService } from '../socket/socket.service'
import { StorageService } from '../storage/storage.service'
import { StorageEvents } from '../storage/storage.types'
import { ServerProxyService } from '../storageServerProxy/storageServerProxy.service'
import { ServerStoredCommunityMetadata } from '../storageServerProxy/storageServerProxy.types'
import { Tor } from '../tor/tor.service'
import { ConfigOptions, GetPorts, ServerIoProviderTypes } from '../types'
import { ServiceState, TorInitState } from './connections-manager.types'

@Injectable()
export class ConnectionsManagerService extends EventEmitter implements OnModuleInit {
Expand All @@ -81,6 +80,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI
@Inject(SOCKS_PROXY_AGENT) public readonly socksProxyAgent: Agent,
private readonly socketService: SocketService,
private readonly registrationService: RegistrationService,
private readonly storageServerProxyService: ServerProxyService,
private readonly localDbService: LocalDbService,
private readonly storageService: StorageService,
private readonly tor: Tor,
Expand Down Expand Up @@ -517,7 +517,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI
agent: this.socksProxyAgent,
localAddress: this.libp2pService.createLibp2pAddress(onionAddress, peerId.toString()),
targetPort: this.ports.libp2pHiddenService,
peers: peers ?? [],
peers: peers ? peers.slice(1) : [],
psk: Libp2pService.generateLibp2pPSK(community.psk).fullKey,
}
await this.libp2pService.createInstance(params)
Expand Down Expand Up @@ -628,6 +628,33 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI
await this.storageService?.saveCSR(payload)
})

this.socketService.on(
SocketActionTypes.DOWNLOAD_INVITE_DATA,
async (payload: { cid: string; serverAddress: string }, callback: (response: CreateNetworkPayload) => void) => {
this.logger(`socketService - ${SocketActionTypes.DOWNLOAD_INVITE_DATA}`)
this.storageServerProxyService.setServerAddress(payload.serverAddress)
let downloadedData: ServerStoredCommunityMetadata
try {
downloadedData = await this.storageServerProxyService.downloadData(payload.cid)
} catch (e) {
this.logger.error(`Downloading community data failed`, e)
emitError(this.serverIoProvider.io, {
type: SocketActionTypes.DOWNLOAD_INVITE_DATA,
message: ErrorMessages.STORAGE_SERVER_CONNECTION_FAILED,
})
return
}

const createNetworkPayload: CreateNetworkPayload = {
ownership: CommunityOwnership.User,
peers: p2pAddressesToPairs(downloadedData.peerList),
psk: downloadedData.psk,
ownerOrbitDbIdentity: downloadedData.ownerOrbitDbIdentity,
}
callback(createNetworkPayload)
}
)

// Public Channels
this.socketService.on(
SocketActionTypes.CREATE_CHANNEL,
Expand Down
8 changes: 5 additions & 3 deletions packages/backend/src/nest/libp2p/libp2p.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Libp2pEvents, Libp2pNodeParams } from './libp2p.types'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import validator from 'validator'
import waitForExpect from 'wait-for-expect'
import { ProcessInChunksService } from './process-in-chunks.service'
import { DEFAULT_NUM_TRIES, ProcessInChunksService } from './process-in-chunks.service'

describe('Libp2pService', () => {
let module: TestingModule
Expand Down Expand Up @@ -93,10 +93,12 @@ describe('Libp2pService', () => {
await libp2pService.createInstance(params)
expect(libp2pService.libp2pInstance).not.toBeNull()
// @ts-expect-error processItem is private
const dialPeerSpy = jest.spyOn(processInChunks, 'processItem')
const processItemSpy = jest.spyOn(processInChunks, 'processItem')
const dialSpy = jest.spyOn(libp2pService.libp2pInstance!, 'dial')
libp2pService.emit(Libp2pEvents.DIAL_PEERS, addresses)
await waitForExpect(async () => {
expect(dialPeerSpy).toBeCalledTimes(1)
expect(processItemSpy).toBeCalledTimes(2 * DEFAULT_NUM_TRIES)
expect(dialSpy).toBeCalledTimes(1)
})
})
})
Loading
Loading