From 4de35704927a0c015e7e998f34b1fcfea9773ade Mon Sep 17 00:00:00 2001 From: William Grant Date: Fri, 22 Mar 2024 17:10:23 +1100 Subject: [PATCH] feat: got things working correctly with errors throwing before the timeout still trying to debug loading the errors in the frontend --- ts/components/registration/hooks/index.tsx | 6 +- .../registration/stages/RestoreAccount.tsx | 56 ++++++++++--------- ts/session/apis/snode_api/swarmPolling.ts | 15 ++--- ts/session/constants.ts | 2 + ts/session/crypto/mnemonic.ts | 20 ++++--- ts/util/accountManager.ts | 20 ++++++- 6 files changed, 69 insertions(+), 50 deletions(-) diff --git a/ts/components/registration/hooks/index.tsx b/ts/components/registration/hooks/index.tsx index 252322efe..5e40190e0 100644 --- a/ts/components/registration/hooks/index.tsx +++ b/ts/components/registration/hooks/index.tsx @@ -35,9 +35,9 @@ export const useRecoveryProgressEffect = (props: UseRecoveryProgressEffectProps) if (progress < 100) { dispatch(setProgress(progress + 1)); } - window.log.debug( - `WIP: [continueYourSession] AccountRestoration.Loading Loading progress ${progress}%` - ); + // window.log.debug( + // `WIP: [continueYourSession] AccountRestoration.Loading Loading progress ${progress}%` + // ); if (progress >= 100) { clearInterval(interval); diff --git a/ts/components/registration/stages/RestoreAccount.tsx b/ts/components/registration/stages/RestoreAccount.tsx index 285655b85..686473440 100644 --- a/ts/components/registration/stages/RestoreAccount.tsx +++ b/ts/components/registration/stages/RestoreAccount.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useDispatch } from 'react-redux'; import { ONBOARDING_TIMES } from '../../../session/constants'; import { InvalidWordsError, NotEnoughWordsError } from '../../../session/crypto/mnemonic'; -import { PromiseUtils, ToastUtils } from '../../../session/utils'; +import { PromiseUtils } from '../../../session/utils'; import { TaskTimedOutError } from '../../../session/utils/Promise'; import { NotFoundError } from '../../../session/utils/errors'; import { @@ -53,7 +53,7 @@ async function signInWithNewDisplayName(signInDetails: RecoverDetails) { await setSignWithRecoveryPhrase(true); } catch (e) { await resetRegistration(); - void errorCallback(e); + errorCallback(e); window.log.debug( `WIP: [signInWithNewDisplayName] exception during registration: ${e.message || e}` ); @@ -70,45 +70,45 @@ async function signInAndFetchDisplayName( loadingAnimationCallback: () => void; } ) { - const { recoveryPassword, errorCallback, loadingAnimationCallback } = signInDetails; + const { recoveryPassword, loadingAnimationCallback } = signInDetails; window.log.debug(`WIP: [signInAndFetchDisplayName] starting sign in....`); let displayNameFromNetwork = ''; + let ourPubkey = ''; try { await resetRegistration(); - void signInByLinkingDevice(recoveryPassword, 'english', loadingAnimationCallback); + const promiseLink = signInByLinkingDevice( + recoveryPassword, + 'english', + loadingAnimationCallback + ); - await PromiseUtils.waitForTask(() => { + const promiseWait = PromiseUtils.waitForTask(done => { window.Whisper.events.on( 'configurationMessageReceived', async (displayName: string, pubkey: string) => { - window.log.debug( - `WIP: [signInAndFetchDisplayName] waitForTask done with displayName: "${displayName}"` - ); window.Whisper.events.off('configurationMessageReceived'); await setSignInByLinking(false); await setSignWithRecoveryPhrase(true); + displayNameFromNetwork = displayName; - await registrationDone(pubkey, displayName); + ourPubkey = pubkey; + done(displayName); } ); }, ONBOARDING_TIMES.RECOVERY_TIMEOUT); - if (!displayNameFromNetwork.length) { - throw new NotFoundError('Got a config message from network but without a displayName...'); - } + await Promise.all([promiseLink, promiseWait]); } catch (e) { await resetRegistration(); - errorCallback(e); + throw e; } - // display name, avatars, groups and contacts should already be handled when this event was triggered. + window.log.debug( - `WIP: [signInAndFetchDisplayName] we got a displayName from network: "${displayNameFromNetwork}"` + `WIP: [signInAndFetchDisplayName] signed in with displayName: "${displayNameFromNetwork}" and pubkey: "${ourPubkey}` ); - - // Do not set the lastProfileUpdateTimestamp. - return displayNameFromNetwork; + return { displayNameFromNetwork, ourPubkey }; } export const RestoreAccount = () => { @@ -135,7 +135,7 @@ export const RestoreAccount = () => { dispatch(setProgress(0)); try { - const displayNameFromNetwork = await signInAndFetchDisplayName({ + const { displayNameFromNetwork, ourPubkey } = await signInAndFetchDisplayName({ recoveryPassword, errorCallback: e => { throw e; @@ -145,29 +145,35 @@ export const RestoreAccount = () => { }, }); dispatch(setDisplayName(displayNameFromNetwork)); + await registrationDone(ourPubkey, displayName); dispatch(setAccountRestorationStep(AccountRestoration.Finishing)); } catch (e) { - if (e instanceof NotFoundError) { + window.log.debug(`WIP: [recoverAndFetchDisplayName] error ${JSON.stringify(e)} `); + if (e instanceof NotFoundError || e instanceof TaskTimedOutError) { window.log.debug( - `WIP: [recoverAndFetchDisplayName] AccountRestoration.RecoveryPassword failed to fetch display name so we need to enter it manually. Error: ${e}` + `WIP: [recoverAndFetchDisplayName] AccountRestoration.RecoveryPassword failed to get a display name so we need to enter it manually. Error: ${e}` ); dispatch(setAccountRestorationStep(AccountRestoration.DisplayName)); return; } + if (e instanceof NotEnoughWordsError) { setRecoveryPasswordError(window.i18n('recoveryPasswordErrorMessageShort')); } else if (e instanceof InvalidWordsError) { setRecoveryPasswordError(window.i18n('recoveryPasswordErrorMessageIncorrect')); - } else if (e instanceof TaskTimedOutError) { - setRecoveryPasswordError(window.i18n('recoveryPasswordErrorMessageGeneric')); - ToastUtils.pushToastError('toolong', e.message || String(e)); } else { setRecoveryPasswordError(window.i18n('recoveryPasswordErrorMessageGeneric')); } window.log.debug( - `WIP: [recoverAndFetchDisplayName] exception during registration: ${e.message || e}` + `WIP: [recoverAndFetchDisplayName] exception during registration: ${e.message || e} type ${typeof e}` + ); + window.log.debug( + `WIP: [recoverAndFetchDisplayName] recoveryPasswordError before: ${recoveryPasswordError}` ); dispatch(setAccountRestorationStep(AccountRestoration.RecoveryPassword)); + window.log.debug( + `WIP: [recoverAndFetchDisplayName] recoveryPasswordError after: ${recoveryPasswordError}` + ); } }; diff --git a/ts/session/apis/snode_api/swarmPolling.ts b/ts/session/apis/snode_api/swarmPolling.ts index e6590ce3b..fde14f2e8 100644 --- a/ts/session/apis/snode_api/swarmPolling.ts +++ b/ts/session/apis/snode_api/swarmPolling.ts @@ -712,21 +712,14 @@ export class SwarmPolling { window.log.info( `[pollOnceForDisplayName] received userConfigMessages count: ${userConfigMessagesMerged.length} for key ${pubkey.key}` ); - try { - const displayName = await this.handleSharedConfigMessages(userConfigMessagesMerged, true); - return displayName; - } catch (e) { - window.log.warn( - `handleSharedConfigMessages of ${userConfigMessagesMerged.length} failed with ${e.message}` - ); - // not rethrowing - } + + const displayName = await this.handleSharedConfigMessages(userConfigMessagesMerged, true); + return displayName; } } return ''; } - // TODO[epic=ses-899] add a function that only polls for the display name? public async pollForOurDisplayName(): Promise { if (!window.getGlobalOnlineStatus()) { window?.log?.error('pollForOurDisplayName: offline'); @@ -741,7 +734,7 @@ export class SwarmPolling { return displayName; } catch (e) { window?.log?.warn('pollForOurDisplayName exception: ', e); - return ''; + throw e; } } } diff --git a/ts/session/constants.ts b/ts/session/constants.ts index d5a139eaa..18b322d0b 100644 --- a/ts/session/constants.ts +++ b/ts/session/constants.ts @@ -79,6 +79,8 @@ export const FEATURE_RELEASE_TIMESTAMPS = { export const ONBOARDING_TIMES = { /** 15 seconds */ RECOVERY_TIMEOUT: 15 * DURATION.SECONDS, + // TODO remove later + // RECOVERY_TIMEOUT: 3 * DURATION.SECONDS, /** 0.3 seconds */ RECOVERY_FINISHING: 0.3 * DURATION.SECONDS, /** 0.2 seconds */ diff --git a/ts/session/crypto/mnemonic.ts b/ts/session/crypto/mnemonic.ts index dae8e3704..d4fd26198 100644 --- a/ts/session/crypto/mnemonic.ts +++ b/ts/session/crypto/mnemonic.ts @@ -3,38 +3,42 @@ import crc32 from 'buffer-crc32'; class MnemonicError extends Error { constructor(message: string) { super(message); + this.name = this.constructor.name; // Set the error name to the class name + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } // restore prototype chain Object.setPrototypeOf(this, MnemonicError.prototype); } } export class NotEnoughWordsError extends MnemonicError { - constructor() { - super("You've entered too few words, please try again"); + constructor(message = "You've entered too few words, please try again") { + super(message); // restore prototype chain Object.setPrototypeOf(this, NotEnoughWordsError.prototype); } } export class InvalidWordsError extends MnemonicError { - constructor() { - super('invalid word in mnemonic'); + constructor(message = "You've entered too few words, please try again") { + super(message); // restore prototype chain Object.setPrototypeOf(this, InvalidWordsError.prototype); } } export class DecodingError extends MnemonicError { - constructor() { - super('Something went wrong when decoding your private key, please try again'); + constructor(message = 'Something went wrong when decoding your private key, please try again') { + super(message); // restore prototype chain Object.setPrototypeOf(this, DecodingError.prototype); } } export class VerificationError extends MnemonicError { - constructor() { - super('Your private key could not be verified, please verify the checksum word'); + constructor(message = 'Your private key could not be verified, please verify the checksum word') { + super(message); // restore prototype chain Object.setPrototypeOf(this, VerificationError.prototype); } diff --git a/ts/util/accountManager.ts b/ts/util/accountManager.ts index 8018ce2ab..c9236e3e1 100644 --- a/ts/util/accountManager.ts +++ b/ts/util/accountManager.ts @@ -1,14 +1,16 @@ +import { isEmpty } from 'lodash'; import { getConversationController } from '../session/conversations'; import { getSodiumRenderer } from '../session/crypto'; import { fromArrayBufferToBase64, fromHex, toHex } from '../session/utils/String'; -import { getOurPubKeyStrFromCache } from '../session/utils/User'; import { configurationMessageReceived, trigger } from '../shims/events'; import { SettingsKey } from '../data/settings-key'; import { ConversationTypeEnum } from '../models/conversationAttributes'; import { SessionKeyPair } from '../receiver/keypairs'; import { getSwarmPollingInstance } from '../session/apis/snode_api'; -import { mnDecode, mnEncode } from '../session/crypto/mnemonic'; +import { NotEnoughWordsError, mnDecode, mnEncode } from '../session/crypto/mnemonic'; +import { getOurPubKeyStrFromCache } from '../session/utils/User'; +import { NotFoundError } from '../session/utils/errors'; import { LibSessionUtil } from '../session/utils/libsession/libsession_utils'; import { actions as userActions } from '../state/ducks/user'; import { Registration } from './registration'; @@ -102,6 +104,16 @@ export async function signInByLinkingDevice( const pubKeyString = toHex(identityKeyPair.pubKey); // fetch configuration message to get the user's display name. const displayName = await getSwarmPollingInstance().pollForOurDisplayName(); + // TODO remove later + throw new NotEnoughWordsError('fml'); + + if (isEmpty(displayName)) { + throw new NotFoundError('Got a config message from network but without a displayName...'); + } + if (isEmpty(pubKeyString)) { + throw new NotFoundError('Got a display name from the network but no a pubkey...'); + } + trigger(configurationMessageReceived, displayName, pubKeyString); } /** @@ -190,7 +202,9 @@ async function createAccount(identityKeyPair: SessionKeyPair) { * @param displayName the display name entered by the user, if any. This is not a display name found from a config message in the network. */ export async function registrationDone(ourPubkey: string, displayName: string) { - window?.log?.info(`registration done with user provided displayName "${displayName}"`); + window?.log?.info( + `registration done with user provided displayName "${displayName}" and pubkey "${ourPubkey}"` + ); // initializeLibSessionUtilWrappers needs our publicKey to be set await Storage.put('primaryDevicePubKey', ourPubkey);