From 26d6a83c4ea3a90904012982188bd3063c0d41d2 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Thu, 21 Mar 2024 09:17:21 -0700 Subject: [PATCH] fix: Move dmKey generation to prevent UI delay during joining --- .../CreateCommunity/CreateCommunity.tsx | 6 +- .../JoinCommunity/JoinCommunity.tsx | 6 +- .../CreateUsername/CreateUsername.tsx | 7 +- .../invitation/customProtocol.saga.test.ts | 18 +-- .../sagas/invitation/customProtocol.saga.ts | 6 +- .../src/integrationTests/appActions.ts | 14 +-- .../src/testUtils/actions.ts | 12 +- .../CreateCommunity.screen.tsx | 6 +- .../JoinCommunity/JoinCommunity.screen.tsx | 6 +- .../store/init/deepLink/deepLink.saga.test.ts | 10 +- .../src/store/init/deepLink/deepLink.saga.ts | 6 +- .../addCommunity/addCommunity.saga.test.ts | 115 ++++++++++++++++++ .../addCommunity/addCommunity.saga.ts | 58 +++++++++ .../communities/communities.master.saga.ts | 2 + .../sagas/communities/communities.slice.ts | 11 +- .../sagas/communities/communities.types.ts | 2 +- .../createNetwork/createNetwork.saga.test.ts | 93 +++++--------- .../createNetwork/createNetwork.saga.ts | 44 +------ .../registerUsername/registerUsername.saga.ts | 3 + packages/types/src/community.ts | 2 +- 20 files changed, 266 insertions(+), 161 deletions(-) create mode 100644 packages/state-manager/src/sagas/communities/addCommunity/addCommunity.saga.test.ts create mode 100644 packages/state-manager/src/sagas/communities/addCommunity/addCommunity.saga.ts diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.tsx index 006c3add8e..13aafd6af9 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/CreateCommunity/CreateCommunity.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { socketSelectors } from '../../../sagas/socket/socket.selectors' import { communities, identity } from '@quiet/state-manager' -import { CommunityOwnership, CreateNetworkPayload } from '@quiet/types' +import { CommunityOwnership, AddCommunityPayload } from '@quiet/types' import PerformCommunityActionComponent from '../PerformCommunityActionComponent' import { ModalName } from '../../../sagas/modals/modals.types' import { useModal } from '../../../containers/hooks' @@ -25,11 +25,11 @@ const CreateCommunity = () => { }, [currentCommunity]) const handleCommunityAction = (name: string) => { - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.Owner, name: name, } - dispatch(communities.actions.createNetwork(payload)) + dispatch(communities.actions.addCommunity(payload)) } // From 'You can join a community instead' link diff --git a/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.tsx b/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.tsx index d4a63352ab..128d1afc7c 100644 --- a/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.tsx +++ b/packages/desktop/src/renderer/components/CreateJoinCommunity/JoinCommunity/JoinCommunity.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { socketSelectors } from '../../../sagas/socket/socket.selectors' -import { CommunityOwnership, CreateNetworkPayload, InvitationData, InvitationPair } from '@quiet/types' +import { CommunityOwnership, AddCommunityPayload, InvitationData, InvitationPair } from '@quiet/types' import { communities, identity, connection, network } from '@quiet/state-manager' import PerformCommunityActionComponent from '../../../components/CreateJoinCommunity/PerformCommunityActionComponent' import { ModalName } from '../../../sagas/modals/modals.types' @@ -39,13 +39,13 @@ const JoinCommunity = () => { }, [currentCommunity]) const handleCommunityAction = (data: InvitationData) => { - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.User, peers: data.pairs, psk: data.psk, ownerOrbitDbIdentity: data.ownerOrbitDbIdentity, } - dispatch(communities.actions.createNetwork(payload)) + dispatch(communities.actions.addCommunity(payload)) } // From 'You can create a new community instead' link diff --git a/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.tsx b/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.tsx index 87f5fe18ee..b4085f1670 100644 --- a/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.tsx +++ b/packages/desktop/src/renderer/components/CreateUsername/CreateUsername.tsx @@ -25,12 +25,7 @@ const CreateUsername = () => { }, [currentIdentity, currentCommunity]) const registerUsername = (nickname: string) => { - dispatch( - identity.actions.registerUsername({ - nickname, - }) - ) - + dispatch(identity.actions.registerUsername({ nickname })) dispatch(network.actions.setLoadingPanelType(LoadingPanelType.Joining)) loadingPanelModal.handleOpen() } diff --git a/packages/desktop/src/renderer/sagas/invitation/customProtocol.saga.test.ts b/packages/desktop/src/renderer/sagas/invitation/customProtocol.saga.test.ts index ac732ee519..ae98e4cec5 100644 --- a/packages/desktop/src/renderer/sagas/invitation/customProtocol.saga.test.ts +++ b/packages/desktop/src/renderer/sagas/invitation/customProtocol.saga.test.ts @@ -1,5 +1,5 @@ import { communities, getFactory, Store } from '@quiet/state-manager' -import { Community, CommunityOwnership, CreateNetworkPayload, InvitationData } from '@quiet/types' +import { Community, CommunityOwnership, AddCommunityPayload, InvitationData } from '@quiet/types' import { FactoryGirl } from 'factory-girl' import { expectSaga } from 'redux-saga-test-plan' import { customProtocolSaga } from './customProtocol.saga' @@ -32,7 +32,7 @@ describe('Handle invitation code', () => { }) it('creates network if code is valid', async () => { - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.User, peers: validInvitationData.pairs, psk: validInvitationData.psk, @@ -40,13 +40,13 @@ describe('Handle invitation code', () => { } await expectSaga(customProtocolSaga, communities.actions.customProtocol(validInvitationData)) .withState(store.getState()) - .put(communities.actions.createNetwork(payload)) + .put(communities.actions.addCommunity(payload)) .run() }) it('does not try to create network if user is already in community', async () => { community = await factory.create['payload']>('Community') - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.User, peers: validInvitationData.pairs, psk: validInvitationData.psk, @@ -63,12 +63,12 @@ describe('Handle invitation code', () => { }, }) ) - .not.put(communities.actions.createNetwork(payload)) + .not.put(communities.actions.addCommunity(payload)) .run() }) it('does not try to create network if code is missing addresses', async () => { - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.User, peers: [], } @@ -92,12 +92,12 @@ describe('Handle invitation code', () => { }, }) ) - .not.put(communities.actions.createNetwork(payload)) + .not.put(communities.actions.addCommunity(payload)) .run() }) it('does not try to create network if code is missing psk', async () => { - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.User, peers: [], } @@ -121,7 +121,7 @@ describe('Handle invitation code', () => { }, }) ) - .not.put(communities.actions.createNetwork(payload)) + .not.put(communities.actions.addCommunity(payload)) .run() }) }) diff --git a/packages/desktop/src/renderer/sagas/invitation/customProtocol.saga.ts b/packages/desktop/src/renderer/sagas/invitation/customProtocol.saga.ts index 98a26e3035..85af17be6b 100644 --- a/packages/desktop/src/renderer/sagas/invitation/customProtocol.saga.ts +++ b/packages/desktop/src/renderer/sagas/invitation/customProtocol.saga.ts @@ -1,6 +1,6 @@ import { PayloadAction } from '@reduxjs/toolkit' import { select, put, delay } from 'typed-redux-saga' -import { CommunityOwnership, CreateNetworkPayload } from '@quiet/types' +import { CommunityOwnership, AddCommunityPayload } from '@quiet/types' import { communities } from '@quiet/state-manager' import { socketSelectors } from '../socket/socket.selectors' import { ModalName } from '../modals/modals.types' @@ -32,13 +32,13 @@ export function* customProtocolSaga( } const invitationData = action.payload if (invitationData && invitationData.pairs.length > 0 && invitationData.psk && invitationData.ownerOrbitDbIdentity) { - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.User, peers: invitationData.pairs, psk: invitationData.psk, ownerOrbitDbIdentity: invitationData.ownerOrbitDbIdentity, } - yield* put(communities.actions.createNetwork(payload)) + yield* put(communities.actions.addCommunity(payload)) } else { yield* put(communities.actions.clearInvitationCodes()) yield* put( diff --git a/packages/integration-tests/src/integrationTests/appActions.ts b/packages/integration-tests/src/integrationTests/appActions.ts index bcbff3e67e..5bcf828226 100644 --- a/packages/integration-tests/src/integrationTests/appActions.ts +++ b/packages/integration-tests/src/integrationTests/appActions.ts @@ -7,7 +7,7 @@ import { connection, publicChannels, RegisterCertificatePayload, - CreateNetworkPayload, + AddCommunityPayload, CommunityOwnership, TestStore, ChannelMessage, @@ -76,12 +76,12 @@ export async function createCommunity({ userName, store }: CreateCommunity) { const timeout = 20_000 const communityName = 'CommunityName' - const createNetworkPayload: CreateNetworkPayload = { + const addCommunityPayload: AddCommunityPayload = { ownership: CommunityOwnership.Owner, name: communityName, } - store.dispatch(communities.actions.createNetwork(createNetworkPayload)) + store.dispatch(communities.actions.addCommunity(addCommunityPayload)) await waitForExpect(() => { expect(store.getState().Identity.identities.ids).toHaveLength(1) @@ -126,11 +126,11 @@ export async function registerUsername(payload: Register) { // Give it a huge timeout, it should never fail, but sometimes takes more time, depending on tor. const timeout = 600_000 - const createNetworkPayload: CreateNetworkPayload = { + const addCommunityPayload: AddCommunityPayload = { ownership: CommunityOwnership.User, } - store.dispatch(communities.actions.createNetwork(createNetworkPayload)) + store.dispatch(communities.actions.addCommunity(addCommunityPayload)) await waitForExpect(() => { expect(store.getState().Identity.identities.ids).toHaveLength(1) @@ -265,11 +265,11 @@ export const sendRegistrationRequest = async (payload: SendRegistrationRequest) const timeout = 600_000 - const createNetworkPayload: CreateNetworkPayload = { + const addCommunityPayload: AddCommunityPayload = { ownership: CommunityOwnership.User, } - store.dispatch(communities.actions.createNetwork(createNetworkPayload)) + store.dispatch(communities.actions.addCommunity(addCommunityPayload)) await waitForExpect(() => { expect(store.getState().Identity.identities.ids).toHaveLength(1) diff --git a/packages/integration-tests/src/testUtils/actions.ts b/packages/integration-tests/src/testUtils/actions.ts index 5a621c0c02..7edaf505e1 100644 --- a/packages/integration-tests/src/testUtils/actions.ts +++ b/packages/integration-tests/src/testUtils/actions.ts @@ -2,7 +2,7 @@ import { ChannelMessage, communities, CommunityOwnership, - CreateNetworkPayload, + AddCommunityPayload, identity, publicChannels, messages, @@ -19,11 +19,11 @@ const timeout = 120_000 export async function registerUsername(payload: Register) { const { userName, store } = payload - const createNetworkPayload: CreateNetworkPayload = { + const addCommunityPayload: AddCommunityPayload = { ownership: CommunityOwnership.User, } - log(`User ${userName} starts creating network`) - store.dispatch(communities.actions.createNetwork(createNetworkPayload)) + log(`User ${userName} starts adding community`) + store.dispatch(communities.actions.addCommunity(addCommunityPayload)) await waitForExpect(() => { assert.equal(store.getState().Identity.identities.ids.length, 1) @@ -46,12 +46,12 @@ export async function registerUsername(payload: Register) { } export const createCommunity = async ({ username, communityName, store }): Promise => { - const createNetworkPayload: CreateNetworkPayload = { + const addCommunityPayload: AddCommunityPayload = { ownership: CommunityOwnership.Owner, name: communityName, } - store.dispatch(communities.actions.createNetwork(createNetworkPayload)) + store.dispatch(communities.actions.addCommunity(addCommunityPayload)) await waitForExpect(() => { assert.strictEqual(store.getState().Identity.identities.ids.length, 1) }, timeout) diff --git a/packages/mobile/src/screens/CreateCommunity/CreateCommunity.screen.tsx b/packages/mobile/src/screens/CreateCommunity/CreateCommunity.screen.tsx index 35ba42cb20..0c2e1d642a 100644 --- a/packages/mobile/src/screens/CreateCommunity/CreateCommunity.screen.tsx +++ b/packages/mobile/src/screens/CreateCommunity/CreateCommunity.screen.tsx @@ -1,7 +1,7 @@ import React, { FC, useCallback } from 'react' import { useDispatch, useSelector } from 'react-redux' import { identity, communities } from '@quiet/state-manager' -import { CommunityOwnership, CreateNetworkPayload } from '@quiet/types' +import { CommunityOwnership, AddCommunityPayload } from '@quiet/types' import { initSelectors } from '../../store/init/init.selectors' import { navigationActions } from '../../store/navigation/navigation.slice' import { ScreenNames } from '../../const/ScreenNames.enum' @@ -19,11 +19,11 @@ export const CreateCommunityScreen: FC = () => { const createCommunityAction = useCallback( (name: string) => { - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.Owner, name, } - dispatch(communities.actions.createNetwork(payload)) + dispatch(communities.actions.addCommunity(payload)) dispatch( navigationActions.navigation({ screen: ScreenNames.UsernameRegistrationScreen, diff --git a/packages/mobile/src/screens/JoinCommunity/JoinCommunity.screen.tsx b/packages/mobile/src/screens/JoinCommunity/JoinCommunity.screen.tsx index 381d6831af..1c63ca75ac 100644 --- a/packages/mobile/src/screens/JoinCommunity/JoinCommunity.screen.tsx +++ b/packages/mobile/src/screens/JoinCommunity/JoinCommunity.screen.tsx @@ -2,7 +2,7 @@ import React, { FC, useCallback, useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { identity, communities } from '@quiet/state-manager' -import { CommunityOwnership, CreateNetworkPayload, InvitationData, InvitationPair } from '@quiet/types' +import { CommunityOwnership, AddCommunityPayload, InvitationData, InvitationPair } from '@quiet/types' import { JoinCommunity } from '../../components/JoinCommunity/JoinCommunity.component' import { navigationActions } from '../../store/navigation/navigation.slice' import { ScreenNames } from '../../const/ScreenNames.enum' @@ -36,13 +36,13 @@ export const JoinCommunityScreen: FC = ({ route }) => const joinCommunityAction = useCallback( (data: InvitationData) => { - const payload: CreateNetworkPayload = { + const payload: AddCommunityPayload = { ownership: CommunityOwnership.User, peers: data.pairs, psk: data.psk, ownerOrbitDbIdentity: data.ownerOrbitDbIdentity, } - dispatch(communities.actions.createNetwork(payload)) + dispatch(communities.actions.addCommunity(payload)) dispatch( navigationActions.navigation({ screen: ScreenNames.UsernameRegistrationScreen, diff --git a/packages/mobile/src/store/init/deepLink/deepLink.saga.test.ts b/packages/mobile/src/store/init/deepLink/deepLink.saga.test.ts index b019eb86c7..b7b901b659 100644 --- a/packages/mobile/src/store/init/deepLink/deepLink.saga.test.ts +++ b/packages/mobile/src/store/init/deepLink/deepLink.saga.test.ts @@ -61,7 +61,7 @@ describe('deepLinkSaga', () => { .withState(store.getState()) .put(initActions.resetDeepLink()) .put( - communities.actions.createNetwork({ + communities.actions.addCommunity({ ownership: CommunityOwnership.User, peers: validData.pairs, psk: validData.psk, @@ -105,7 +105,7 @@ describe('deepLinkSaga', () => { .withReducer(reducer) .withState(store.getState()) .not.put( - communities.actions.createNetwork({ + communities.actions.addCommunity({ ownership: CommunityOwnership.User, peers: validData.pairs, psk: validData.psk, @@ -149,7 +149,7 @@ describe('deepLinkSaga', () => { }, }) .not.put( - communities.actions.createNetwork({ + communities.actions.addCommunity({ ownership: CommunityOwnership.User, peers: validData.pairs, psk: validData.psk, @@ -192,7 +192,7 @@ describe('deepLinkSaga', () => { }) .put.like({ action: { - type: communities.actions.createNetwork.type, + type: communities.actions.addCommunity.type, payload: { ownership: CommunityOwnership.User, peers: validData.pairs, @@ -238,7 +238,7 @@ describe('deepLinkSaga', () => { }, }) .not.put( - communities.actions.createNetwork({ + communities.actions.addCommunity({ ownership: CommunityOwnership.User, peers: validData.pairs, psk: validData.psk, diff --git a/packages/mobile/src/store/init/deepLink/deepLink.saga.ts b/packages/mobile/src/store/init/deepLink/deepLink.saga.ts index 4c98593786..6ce86d8f81 100644 --- a/packages/mobile/src/store/init/deepLink/deepLink.saga.ts +++ b/packages/mobile/src/store/init/deepLink/deepLink.saga.ts @@ -7,7 +7,7 @@ import { initSelectors } from '../init.selectors' import { initActions } from '../init.slice' import { appImages } from '../../../assets' import { replaceScreen } from '../../../RootNavigation' -import { CommunityOwnership, CreateNetworkPayload, InvitationData } from '@quiet/types' +import { CommunityOwnership, AddCommunityPayload, InvitationData } from '@quiet/types' import { areObjectsEqual } from '../../../utils/functions/areObjectsEqual/areObjectsEqual' export function* deepLinkSaga(action: PayloadAction['payload']>): Generator { @@ -127,14 +127,14 @@ export function* deepLinkSaga(action: PayloadAction { + it('add community for joining user', async () => { + setupCrypto() + + const socket = { + emit: jest.fn(), + emitWithAck: jest.fn(() => { + return {} + }), + on: jest.fn(), + } as unknown as Socket + + const store = prepareStore().store + + const peers = [{ peerId: 'peerId', onionAddress: 'address' }] + const psk = '12345' + + const community: Community = { + id: '1', + name: undefined, + CA: null, + rootCa: undefined, + psk, + ownerOrbitDbIdentity: undefined, + } + + const reducer = combineReducers(reducers) + await expectSaga( + addCommunitySaga, + socket, + communitiesActions.addCommunity({ + ownership: CommunityOwnership.User, + peers, + psk, + }) + ) + .withReducer(reducer) + .withState(store.getState()) + .provide([[call.fn(generateId), community.id]]) + .not.call(createRootCA) + .call(generateId) + .put(communitiesActions.setInvitationCodes(peers)) + .put(communitiesActions.savePSK(psk)) + .put(communitiesActions.addNewCommunity(community)) + .put(communitiesActions.setCurrentCommunity(community.id)) + .run() + }) + + it('add community for owner', async () => { + setupCrypto() + + const socket = { + emit: jest.fn(), + emitWithAck: jest.fn(() => { + return {} + }), + on: jest.fn(), + } as unknown as Socket + + const store = prepareStore().store + + const CA = { + rootCertString: 'rootCertString', + rootKeyString: 'rootKeyString', + } + + const community: Community = { + id: '1', + name: 'rockets', + CA, + rootCa: CA.rootCertString, + psk: undefined, + ownerOrbitDbIdentity: undefined, + } + + const reducer = combineReducers(reducers) + await expectSaga( + addCommunitySaga, + socket, + communitiesActions.addCommunity({ + ownership: CommunityOwnership.Owner, + name: community.name, + }) + ) + .withReducer(reducer) + .withState(store.getState()) + .provide([ + [call.fn(createRootCA), CA], + [call.fn(generateId), community.id], + ]) + .call( + createRootCA, + new Time({ type: 0, value: new Date(Date.UTC(2010, 11, 28, 10, 10, 10)) }), + new Time({ type: 0, value: new Date(Date.UTC(2030, 11, 28, 10, 10, 10)) }), + community.name + ) + .call(generateId) + .put(communitiesActions.addNewCommunity(community)) + .put(communitiesActions.setCurrentCommunity(community.id)) + .run() + }) +}) diff --git a/packages/state-manager/src/sagas/communities/addCommunity/addCommunity.saga.ts b/packages/state-manager/src/sagas/communities/addCommunity/addCommunity.saga.ts new file mode 100644 index 0000000000..989d676d95 --- /dev/null +++ b/packages/state-manager/src/sagas/communities/addCommunity/addCommunity.saga.ts @@ -0,0 +1,58 @@ +import { PayloadAction } from '@reduxjs/toolkit' +import { call, put } from 'typed-redux-saga' +import { Time } from 'pkijs' +import { generateId } from '../../../utils/cryptography/cryptography' +import { communitiesActions } from '../communities.slice' +import { identityActions } from '../../identity/identity.slice' +import { createRootCA } from '@quiet/identity' +import { type Community, CommunityOwnership, type Identity, SocketActionTypes } from '@quiet/types' +import { Socket } from '../../../types' + +/** + * Generate root CA cert and add initial Community data to Redux Store + */ +export function* addCommunitySaga( + socket: Socket, + action: PayloadAction['payload']> +) { + let CA: null | { + rootCertString: string + rootKeyString: string + } = null + + if (action.payload.ownership === CommunityOwnership.Owner) { + const notBeforeDate = new Date(Date.UTC(2010, 11, 28, 10, 10, 10)) + const notAfterDate = new Date(Date.UTC(2030, 11, 28, 10, 10, 10)) + + CA = yield* call( + createRootCA, + new Time({ type: 0, value: notBeforeDate }), + new Time({ type: 0, value: notAfterDate }), + action.payload.name + ) + } + + const id = yield* call(generateId) + const community: Community = { + id, + name: action.payload.name, + CA, + rootCa: CA?.rootCertString, + psk: action.payload.psk, + ownerOrbitDbIdentity: action.payload.ownerOrbitDbIdentity, + } + + const invitationPeers = action.payload.peers + if (invitationPeers) { + yield* put(communitiesActions.setInvitationCodes(invitationPeers)) + } + + const psk = action.payload.psk + if (psk) { + console.log('addCommunitySaga: saving PSK') + yield* put(communitiesActions.savePSK(psk)) + } + + yield* put(communitiesActions.addNewCommunity(community)) + yield* put(communitiesActions.setCurrentCommunity(id)) +} diff --git a/packages/state-manager/src/sagas/communities/communities.master.saga.ts b/packages/state-manager/src/sagas/communities/communities.master.saga.ts index 4188795afb..71907671ce 100644 --- a/packages/state-manager/src/sagas/communities/communities.master.saga.ts +++ b/packages/state-manager/src/sagas/communities/communities.master.saga.ts @@ -8,9 +8,11 @@ import { createNetworkSaga } from './createNetwork/createNetwork.saga' import { saveCommunityMetadataSaga } from './saveCommunityMetadata/saveCommunityMetadata.saga' import { sendCommunityMetadataSaga } from './updateCommunityMetadata/updateCommunityMetadata.saga' import { sendCommunityCaDataSaga } from './sendCommunityCaData/sendCommunityCaData.saga' +import { addCommunitySaga } from './addCommunity/addCommunity.saga' export function* communitiesMasterSaga(socket: Socket): Generator { yield all([ + takeEvery(communitiesActions.addCommunity.type, addCommunitySaga, socket), takeEvery(communitiesActions.createNetwork.type, createNetworkSaga, socket), takeEvery(communitiesActions.updateCommunity.type, updateCommunitySaga), takeEvery(connectionActions.torBootstrapped.type, initCommunities), diff --git a/packages/state-manager/src/sagas/communities/communities.slice.ts b/packages/state-manager/src/sagas/communities/communities.slice.ts index 188c75ca6b..0cbd9d5605 100644 --- a/packages/state-manager/src/sagas/communities/communities.slice.ts +++ b/packages/state-manager/src/sagas/communities/communities.slice.ts @@ -5,7 +5,7 @@ import { InvitationPair, type AddOwnerCertificatePayload, type Community, - type CreateNetworkPayload, + type AddCommunityPayload, type StorePeerListPayload, CommunityMetadata, InvitationData, @@ -22,12 +22,13 @@ export const communitiesSlice = createSlice({ initialState: { ...new CommunitiesState() }, name: StoreKeys.Communities, reducers: { - setCurrentCommunity: (state, action: PayloadAction) => { - state.currentCommunity = action.payload - }, + addCommunity: (state, _action: PayloadAction) => state, addNewCommunity: (state, action: PayloadAction) => { communitiesAdapter.addOne(state.communities, action.payload) }, + setCurrentCommunity: (state, action: PayloadAction) => { + state.currentCommunity = action.payload + }, updateCommunity: (state, _action: PayloadAction) => state, updateCommunityData: (state, action: PayloadAction) => { communitiesAdapter.updateOne(state.communities, { @@ -39,7 +40,7 @@ export const communitiesSlice = createSlice({ }, sendCommunityCaData: state => state, sendCommunityMetadata: state => state, - createNetwork: (state, _action: PayloadAction) => state, + createNetwork: state => state, storePeerList: (state, action: PayloadAction) => { communitiesAdapter.updateOne(state.communities, { id: action.payload.communityId, diff --git a/packages/state-manager/src/sagas/communities/communities.types.ts b/packages/state-manager/src/sagas/communities/communities.types.ts index 8ac9db480d..7d542eb290 100644 --- a/packages/state-manager/src/sagas/communities/communities.types.ts +++ b/packages/state-manager/src/sagas/communities/communities.types.ts @@ -7,7 +7,7 @@ export enum CommunityOwnership { User = 'user', } -export interface CreateNetworkPayload { +export interface AddCommunityPayload { ownership: CommunityOwnership name?: string } diff --git a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.test.ts b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.test.ts index 301db9617c..d765b558fe 100644 --- a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.test.ts +++ b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.test.ts @@ -4,21 +4,27 @@ import { call } from 'redux-saga-test-plan/matchers' import { Time } from 'pkijs' import { prepareStore } from '../../../utils/tests/prepareStore' import { communitiesActions } from '../communities.slice' +import { identityActions } from '../../identity/identity.slice' import { createRootCA, setupCrypto } from '@quiet/identity' import { reducers } from '../../reducers' import { createNetworkSaga } from './createNetwork.saga' -import { generateId } from '../../../utils/cryptography/cryptography' -import { type Community, CommunityOwnership } from '@quiet/types' -import { Socket } from '../../../types' +import { generateDmKeyPair } from '../../../utils/cryptography/cryptography' +import { type Community, CommunityOwnership, SocketActionTypes } from '@quiet/types' +import { Socket, applyEmitParams } from '../../../types' describe('createNetwork', () => { - it('create network for joining user', async () => { + it('creates network', async () => { setupCrypto() + const network = { + hiddenService: 'testHiddenService', + peerId: 'testPeerId', + } + const socket = { emit: jest.fn(), emitWithAck: jest.fn(() => { - return {} + return network }), on: jest.fn(), } as unknown as Socket @@ -27,77 +33,34 @@ describe('createNetwork', () => { const community: Community = { id: '1', - name: undefined, + name: 'test', CA: null, rootCa: undefined, } - const reducer = combineReducers(reducers) - await expectSaga( - createNetworkSaga, - socket, - communitiesActions.createNetwork({ - ownership: CommunityOwnership.User, - peers: [{ peerId: 'peerId', onionAddress: 'address' }], - psk: '12345', - }) - ) - .withReducer(reducer) - .withState(store.getState()) - .provide([[call.fn(generateId), community.id]]) - .not.call(createRootCA) - .call(generateId) - .run() - }) + store.dispatch(communitiesActions.addNewCommunity(community)) + store.dispatch(communitiesActions.setCurrentCommunity(community.id)) - it('create network for owner', async () => { - setupCrypto() + const dmKeys = 'testDmKeyPair' - const socket = { - emit: jest.fn(), - emitWithAck: jest.fn(() => { - return {} - }), - on: jest.fn(), - } as unknown as Socket - - const store = prepareStore().store - - const CA = { - rootCertString: 'rootCertString', - rootKeyString: 'rootKeyString', - } - - const community: Community = { - id: '1', - name: 'rockets', - CA, - rootCa: CA.rootCertString, + const identity = { + id: community.id, + nickname: '', + hiddenService: network.hiddenService, + peerId: network.peerId, + dmKeys, + userCsr: null, + userCertificate: null, + joinTimestamp: null, } const reducer = combineReducers(reducers) - await expectSaga( - createNetworkSaga, - socket, - communitiesActions.createNetwork({ - ownership: CommunityOwnership.Owner, - name: 'rockets', - psk: '12345', - }) - ) + await expectSaga(createNetworkSaga, socket, communitiesActions.createNetwork()) .withReducer(reducer) .withState(store.getState()) - .provide([ - [call.fn(createRootCA), CA], - [call.fn(generateId), community.id], - ]) - .call( - createRootCA, - new Time({ type: 0, value: new Date(Date.UTC(2010, 11, 28, 10, 10, 10)) }), - new Time({ type: 0, value: new Date(Date.UTC(2030, 11, 28, 10, 10, 10)) }), - 'rockets' - ) - .call(generateId) + .apply(socket, socket.emitWithAck, applyEmitParams(SocketActionTypes.CREATE_NETWORK, community.id)) + .provide([[call.fn(generateDmKeyPair), dmKeys]]) + .put(identityActions.addNewIdentity(identity)) .run() }) }) diff --git a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts index 69a80947ac..48a27cd0c9 100644 --- a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts +++ b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts @@ -1,8 +1,9 @@ import { PayloadAction } from '@reduxjs/toolkit' -import { apply, call, put } from 'typed-redux-saga' +import { apply, call, put, select } from 'typed-redux-saga' import { Time } from 'pkijs' import { generateId } from '../../../utils/cryptography/cryptography' import { communitiesActions } from '../communities.slice' +import { communitiesSelectors } from '../../communities/communities.selectors' import { identityActions } from '../../identity/identity.slice' import { createRootCA } from '@quiet/identity' import { type Community, CommunityOwnership, type Identity, SocketActionTypes } from '@quiet/types' @@ -14,47 +15,14 @@ export function* createNetworkSaga( action: PayloadAction['payload']> ) { console.log('create network saga') - let CA: null | { - rootCertString: string - rootKeyString: string - } = null - if (action.payload.ownership === CommunityOwnership.Owner) { - const notBeforeDate = new Date(Date.UTC(2010, 11, 28, 10, 10, 10)) - const notAfterDate = new Date(Date.UTC(2030, 11, 28, 10, 10, 10)) + const community = yield* select(communitiesSelectors.currentCommunity) - CA = yield* call( - createRootCA, - new Time({ type: 0, value: notBeforeDate }), - new Time({ type: 0, value: notAfterDate }), - action.payload.name - ) + if (!community) { + console.error('Could not create network, no community') + return } - const id = yield* call(generateId) - const community: Community = { - id, - name: action.payload.name, - CA, - rootCa: CA?.rootCertString, - psk: action.payload.psk, - ownerOrbitDbIdentity: action.payload.ownerOrbitDbIdentity, - } - - const invitationPeers = action.payload.peers - if (invitationPeers) { - yield* put(communitiesActions.setInvitationCodes(invitationPeers)) - } - - const psk = action.payload.psk - if (psk) { - console.log('create network saga: saving PSK') - yield* put(communitiesActions.savePSK(psk)) - } - - yield* put(communitiesActions.addNewCommunity(community)) - yield* put(communitiesActions.setCurrentCommunity(id)) - const network = yield* apply( socket, socket.emitWithAck, diff --git a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts index 6899f03c21..ba6829b5db 100644 --- a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts +++ b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts @@ -5,6 +5,7 @@ import { identitySelectors } from '../identity.selectors' import { identityActions } from '../identity.slice' import { config } from '../../users/const/certFieldTypes' import { Socket } from '../../../types' +import { communitiesActions } from '../../communities/communities.slice' import { communitiesSelectors } from '../../communities/communities.selectors' import { CreateUserCsrPayload, RegisterCertificatePayload, Community } from '@quiet/types' @@ -23,6 +24,8 @@ export function* registerUsernameSaga( return } + yield* put(communitiesActions.createNetwork()) + let identity = yield* select(identitySelectors.currentIdentity) if (!identity) { diff --git a/packages/types/src/community.ts b/packages/types/src/community.ts index d952fecfbb..425a765d17 100644 --- a/packages/types/src/community.ts +++ b/packages/types/src/community.ts @@ -23,7 +23,7 @@ export enum CommunityOwnership { User = 'user', } -export interface CreateNetworkPayload { +export interface AddCommunityPayload { ownership: CommunityOwnership name?: string peers?: InvitationPair[]