diff --git a/src/app/modules/shared_modules/keycard_popup/controller.nim b/src/app/modules/shared_modules/keycard_popup/controller.nim index 475dec8b31..4cdcb9b42c 100644 --- a/src/app/modules/shared_modules/keycard_popup/controller.nim +++ b/src/app/modules/shared_modules/keycard_popup/controller.nim @@ -373,8 +373,7 @@ proc convertSelectedKeyPairToKeycardAccount*(self: Controller, password: string) return let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true) singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW) - self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid, - currentPassword = password, + self.accountsService.convertToKeycardAccount(currentPassword = password, newPassword = acc.derivedAccounts.encryption.publicKey) proc getConvertingProfileSuccess*(self: Controller): bool = diff --git a/src/app/modules/startup/controller.nim b/src/app/modules/startup/controller.nim index cfaf4d19ae..d613e17ec7 100644 --- a/src/app/modules/startup/controller.nim +++ b/src/app/modules/startup/controller.nim @@ -338,7 +338,7 @@ proc importMnemonic*(self: Controller): bool = self.delegate.importAccountSuccess() return true else: - self.delegate.importAccountError(error) + self.delegate.emitStartupError(error, StartupErrorType.ImportAccError) return false proc setupKeychain(self: Controller, store: bool) = @@ -352,7 +352,7 @@ proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool) = self.delegate.moveToLoadingAppState() let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName) if error != "": - self.delegate.setupAccountError(error) + self.delegate.emitStartupError(error, StartupErrorType.SetupAccError) else: self.setupKeychain(storeToKeychain) @@ -388,7 +388,7 @@ proc setupKeycardAccount*(self: Controller, storeToKeychain: bool, newKeycard: b else: if self.tmpKeycardEvent.keyUid.len == 0 or self.accountsService.openedAccountsContainsKeyUid(self.tmpKeycardEvent.keyUid): - self.delegate.importAccountError(ACCOUNT_ALREADY_EXISTS_ERROR) + self.delegate.emitStartupError(ACCOUNT_ALREADY_EXISTS_ERROR, StartupErrorType.ImportAccError) return self.delegate.moveToLoadingAppState() if newKeycard: @@ -441,19 +441,43 @@ proc login*(self: Controller) = if(error.len > 0): self.delegate.emitAccountLoginError(error) -proc loginAccountKeycard*(self: Controller, storeToKeychain = false, syncWalletAfterLogin = false) = +proc loginAccountKeycard*(self: Controller, storeToKeychainValue: string, syncWalletAfterLogin = false) = if syncWalletAfterLogin: self.syncKeycardBasedOnAppWalletStateAfterLogin() + singletonInstance.localAccountSettings.setStoreToKeychainValue(storeToKeychainValue) + self.delegate.moveToLoadingAppState() + let selAcc = self.getSelectedLoginAccount() + let error = self.accountsService.loginAccountKeycard(selAcc, self.tmpKeycardEvent) + if(error.len > 0): + self.delegate.emitAccountLoginError(error) + +proc loginAccountKeycardUsingSeedPhrase*(self: Controller, storeToKeychain: bool) = + let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true, includeWhisper = true) + let selAcc = self.getSelectedLoginAccount() + + var kcData = KeycardEvent( + keyUid: acc.keyUid, + masterKey: KeyDetails(address: acc.address), + whisperKey: KeyDetails(privateKey: acc.derivedAccounts.whisper.privateKey), + encryptionKey: KeyDetails(publicKey: acc.derivedAccounts.encryption.publicKey) + ) + if acc.derivedAccounts.whisper.privateKey.startsWith("0x"): + kcData.whisperKey.privateKey = acc.derivedAccounts.whisper.privateKey[2..^1] + if storeToKeychain: ## storing not now, user will be asked to store the pin once he is logged in singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW) else: singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER) + self.delegate.moveToLoadingAppState() - let error = self.accountsService.loginAccountKeycard(self.tmpKeycardEvent) + let error = self.accountsService.loginAccountKeycard(selAcc, kcData) if(error.len > 0): self.delegate.emitAccountLoginError(error) +proc convertToRegularAccount*(self: Controller): string = + let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true) + return self.accountsService.convertToRegularAccount(self.getSeedPhrase(), acc.derivedAccounts.encryption.publicKey, self.getPassword()) proc getKeyUidForSeedPhrase*(self: Controller, seedPhrase: string): string = let acc = self.accountsService.createAccountFromMnemonic(seedPhrase) return acc.keyUid diff --git a/src/app/modules/startup/internal/biometrics_state.nim b/src/app/modules/startup/internal/biometrics_state.nim index cb7a345eb7..63c17650f0 100644 --- a/src/app/modules/startup/internal/biometrics_state.nim +++ b/src/app/modules/startup/internal/biometrics_state.nim @@ -29,6 +29,8 @@ method executePrimaryCommand*(self: BiometricsState, controller: Controller) = elif self.flowType == FlowType.LostKeycardReplacement: self.storeToKeychain = storeToKeychain controller.startLoginFlowAutomatically(controller.getPin()) + elif self.flowType == FlowType.LostKeycardConvertToRegularAccount: + controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain) method executeSecondaryCommand*(self: BiometricsState, controller: Controller) = let storeToKeychain = false # false, cause we don't have keychain support for other than mac os @@ -49,6 +51,8 @@ method executeSecondaryCommand*(self: BiometricsState, controller: Controller) = elif self.flowType == FlowType.LostKeycardReplacement: self.storeToKeychain = storeToKeychain controller.startLoginFlowAutomatically(controller.getPin()) + elif self.flowType == FlowType.LostKeycardConvertToRegularAccount: + controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain) method resolveKeycardNextState*(self: BiometricsState, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State = @@ -56,4 +60,7 @@ method resolveKeycardNextState*(self: BiometricsState, keycardFlowType: string, if keycardFlowType == ResponseTypeValueKeycardFlowResult and keycardEvent.error.len == 0: controller.setKeycardEvent(keycardEvent) - controller.loginAccountKeycard(self.storeToKeychain, syncWalletAfterLogin = true) \ No newline at end of file + var storeToKeychainValue = LS_VALUE_NEVER + if self.storeToKeychain: + storeToKeychainValue = LS_VALUE_NOT_NOW + controller.loginAccountKeycard(storeToKeychainValue, syncWalletAfterLogin = true) \ No newline at end of file diff --git a/src/app/modules/startup/internal/keycard_enter_puk_state.nim b/src/app/modules/startup/internal/keycard_enter_puk_state.nim index 9b2d36af80..ee0aec5b14 100644 --- a/src/app/modules/startup/internal/keycard_enter_puk_state.nim +++ b/src/app/modules/startup/internal/keycard_enter_puk_state.nim @@ -56,5 +56,6 @@ method resolveKeycardNextState*(self: KeycardEnterPukState, keycardFlowType: str if keycardFlowType == ResponseTypeValueKeycardFlowResult: controller.setKeycardEvent(keycardEvent) controller.setPukValid(true) - controller.loginAccountKeycard() + let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue() + controller.loginAccountKeycard(storeToKeychainValue) return nil \ No newline at end of file diff --git a/src/app/modules/startup/internal/keycard_pin_set_state.nim b/src/app/modules/startup/internal/keycard_pin_set_state.nim index 3bdd6b445c..9dcd2f796c 100644 --- a/src/app/modules/startup/internal/keycard_pin_set_state.nim +++ b/src/app/modules/startup/internal/keycard_pin_set_state.nim @@ -44,7 +44,8 @@ method executePrimaryCommand*(self: KeycardPinSetState, controller: Controller) controller.startLoginFlowAutomatically(controller.getPin()) return if controller.getValidPuk(): - controller.loginAccountKeycard() + let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue() + controller.loginAccountKeycard(storeToKeychainValue) if self.flowType == FlowType.LostKeycardReplacement: if main_constants.IS_MACOS: return @@ -56,10 +57,10 @@ method resolveKeycardNextState*(self: KeycardPinSetState, keycardFlowType: strin if keycardFlowType == ResponseTypeValueKeycardFlowResult and keycardEvent.error.len == 0: controller.setKeycardEvent(keycardEvent) - controller.loginAccountKeycard(storeToKeychain = true, syncWalletAfterLogin = true) + controller.loginAccountKeycard(storeToKeychainValue = LS_VALUE_NOT_NOW, syncWalletAfterLogin = true) if self.flowType == FlowType.AppLogin: if keycardFlowType == ResponseTypeValueKeycardFlowResult and keycardEvent.error.len == 0: # we are here in case of recover account from the login flow using seed phrase controller.setKeycardEvent(keycardEvent) - controller.loginAccountKeycard(storeToKeychain = false, syncWalletAfterLogin = false) \ No newline at end of file + controller.loginAccountKeycard(storeToKeychainValue = LS_VALUE_NEVER, syncWalletAfterLogin = false) \ No newline at end of file diff --git a/src/app/modules/startup/internal/keycard_wrong_keycard_state.nim b/src/app/modules/startup/internal/keycard_wrong_keycard_state.nim index f6b275c5d9..8faf8c415f 100644 --- a/src/app/modules/startup/internal/keycard_wrong_keycard_state.nim +++ b/src/app/modules/startup/internal/keycard_wrong_keycard_state.nim @@ -10,6 +10,5 @@ proc delete*(self: KeycardWrongKeycardState) = method executeBackCommand*(self: KeycardWrongKeycardState, controller: Controller) = if self.flowType == FlowType.FirstRunOldUserKeycardImport or - self.flowType == FlowType.AppLogin or - self.flowType == FlowType.LostKeycardReplacement: + self.flowType == FlowType.AppLogin: controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false)) \ No newline at end of file diff --git a/src/app/modules/startup/internal/keycard_wrong_puk_state.nim b/src/app/modules/startup/internal/keycard_wrong_puk_state.nim index 8c2165f555..d1e5779144 100644 --- a/src/app/modules/startup/internal/keycard_wrong_puk_state.nim +++ b/src/app/modules/startup/internal/keycard_wrong_puk_state.nim @@ -59,5 +59,6 @@ method resolveKeycardNextState*(self: KeycardWrongPukState, keycardFlowType: str if keycardFlowType == ResponseTypeValueKeycardFlowResult: controller.setKeycardEvent(keycardEvent) controller.setPukValid(true) - controller.loginAccountKeycard() + let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue() + controller.loginAccountKeycard(storeToKeychainValue) return nil \ No newline at end of file diff --git a/src/app/modules/startup/internal/login_keycard_converted_to_regular_account_state.nim b/src/app/modules/startup/internal/login_keycard_converted_to_regular_account_state.nim new file mode 100644 index 0000000000..ef6b271500 --- /dev/null +++ b/src/app/modules/startup/internal/login_keycard_converted_to_regular_account_state.nim @@ -0,0 +1,14 @@ +type + LoginKeycardConvertedToRegularAccountState* = ref object of State + +proc newLoginKeycardConvertedToRegularAccountState*(flowType: FlowType, backState: State): LoginKeycardConvertedToRegularAccountState = + result = LoginKeycardConvertedToRegularAccountState() + result.setup(flowType, StateType.LoginKeycardConvertedToRegularAccount, backState) + +proc delete*(self: LoginKeycardConvertedToRegularAccountState) = + self.State.delete + +method executePrimaryCommand*(self: LoginKeycardConvertedToRegularAccountState, controller: Controller) = + if self.flowType == FlowType.LostKeycardConvertToRegularAccount: + info "restart the app because of successfully converted keycard account to regular account" + quit() # quit the app \ No newline at end of file diff --git a/src/app/modules/startup/internal/login_keycard_pin_verified_state.nim b/src/app/modules/startup/internal/login_keycard_pin_verified_state.nim index 235f38a59e..6d11299199 100644 --- a/src/app/modules/startup/internal/login_keycard_pin_verified_state.nim +++ b/src/app/modules/startup/internal/login_keycard_pin_verified_state.nim @@ -10,4 +10,5 @@ proc delete*(self: LoginKeycardPinVerifiedState) = method executePrimaryCommand*(self: LoginKeycardPinVerifiedState, controller: Controller) = if self.flowType == FlowType.AppLogin: - controller.loginAccountKeycard() \ No newline at end of file + let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue() + controller.loginAccountKeycard(storeToKeychainValue) \ No newline at end of file diff --git a/src/app/modules/startup/internal/lost_keycard_options_state.nim b/src/app/modules/startup/internal/lost_keycard_options_state.nim index 342f8ee2b5..2398bdf70b 100644 --- a/src/app/modules/startup/internal/lost_keycard_options_state.nim +++ b/src/app/modules/startup/internal/lost_keycard_options_state.nim @@ -19,8 +19,9 @@ method executePrimaryCommand*(self: LostKeycardOptionsState, controller: Control self.setFlowType(FlowType.LostKeycardReplacement) controller.runLoadAccountFlow() -method executeSecondaryCommand*(self: LostKeycardOptionsState, controller: Controller) = - echo "TODO: start using account without keycard..." +method getNextSecondaryState*(self: LostKeycardOptionsState, controller: Controller): State = + if controller.isSelectedAccountAKeycardAccount(): + return createState(StateType.UserProfileEnterSeedPhrase, FlowType.LostKeycardConvertToRegularAccount, self) method resolveKeycardNextState*(self: LostKeycardOptionsState, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State = diff --git a/src/app/modules/startup/internal/state.nim b/src/app/modules/startup/internal/state.nim index 18b758a74b..d4f1afc45c 100644 --- a/src/app/modules/startup/internal/state.nim +++ b/src/app/modules/startup/internal/state.nim @@ -14,6 +14,7 @@ type FlowType* {.pure.} = enum FirstRunOldUserImportSeedPhrase = "FirstRunOldUserImportSeedPhrase" AppLogin = "AppLogin" LostKeycardReplacement = "LostKeycardReplacement" + LostKeycardConvertToRegularAccount = "LostKeycardConvertToRegularAccount" type StateType* {.pure.} = enum NoState = "NoState" @@ -29,6 +30,7 @@ type StateType* {.pure.} = enum UserProfileConfirmPassword = "UserProfileConfirmPassword" UserProfileImportSeedPhrase = "UserProfileImportSeedPhrase" UserProfileEnterSeedPhrase = "UserProfileEnterSeedPhrase" + UserProfileWrongSeedPhrase = "UserProfileWrongSeedPhrase" Biometrics = "Biometrics" KeycardPluginReader = "KeycardPluginReader" KeycardInsertKeycard = "KeycardInsertKeycard" @@ -69,6 +71,7 @@ type StateType* {.pure.} = enum LoginKeycardMaxPairingSlotsReached = "LoginKeycardMaxPairingSlotsReached" LoginKeycardEmpty = "LoginKeycardEmpty" LoginNotKeycard = "LoginNotKeycard" + LoginKeycardConvertedToRegularAccount = "LoginKeycardConvertedToRegularAccount" ProfileFetching = "ProfileFetching" ProfileFetchingSuccess = "ProfileFetchingSuccess" ProfileFetchingTimeout = "ProfileFetchingTimeout" diff --git a/src/app/modules/startup/internal/state_factory.nim b/src/app/modules/startup/internal/state_factory.nim index 6800f5816a..5eeb6c8690 100644 --- a/src/app/modules/startup/internal/state_factory.nim +++ b/src/app/modules/startup/internal/state_factory.nim @@ -1,4 +1,5 @@ import sequtils, sugar, chronicles +import ../../../global/global_singleton import ../../../../constants as main_constants import ../../../../app_service/service/keycard/constants import ../controller @@ -54,12 +55,14 @@ include user_profile_create_password_state include user_profile_create_state include user_profile_create_same_chat_key_state include user_profile_enter_seed_phrase_state +include user_profile_wrong_seed_phrase_state include user_profile_import_seed_phrase_state include welcome_state_new_user include welcome_state_old_user include welcome_state include login_state include login_plugin_state +include login_keycard_converted_to_regular_account_state include login_keycard_insert_keycard_state include login_keycard_inserted_keycard_state include login_keycard_reading_keycard_state diff --git a/src/app/modules/startup/internal/state_factory_general_implementation.nim b/src/app/modules/startup/internal/state_factory_general_implementation.nim index d8d20e4651..267e6f7c9c 100644 --- a/src/app/modules/startup/internal/state_factory_general_implementation.nim +++ b/src/app/modules/startup/internal/state_factory_general_implementation.nim @@ -21,6 +21,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St return newUserProfileImportSeedPhraseState(flowType, backState) if stateToBeCreated == StateType.UserProfileEnterSeedPhrase: return newUserProfileEnterSeedPhraseState(flowType, backState) + if stateToBeCreated == StateType.UserProfileWrongSeedPhrase: + return newUserProfileWrongSeedPhraseState(flowType, backState) if stateToBeCreated == StateType.Biometrics: return newBiometricsState(flowType, backState) if stateToBeCreated == StateType.KeycardPluginReader: @@ -73,6 +75,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St return newLoginState(flowType, backState) if stateToBeCreated == StateType.LoginPlugin: return newLoginPluginState(flowType, backState) + if stateToBeCreated == StateType.LoginKeycardConvertedToRegularAccount: + return newLoginKeycardConvertedToRegularAccountState(flowType, backState) if stateToBeCreated == StateType.LoginKeycardInsertKeycard: return newLoginKeycardInsertKeycardState(flowType, backState) if stateToBeCreated == StateType.LoginKeycardInsertedKeycard: diff --git a/src/app/modules/startup/internal/user_profile_confirm_password_state.nim b/src/app/modules/startup/internal/user_profile_confirm_password_state.nim index c37b75b2e2..d14ec009c9 100644 --- a/src/app/modules/startup/internal/user_profile_confirm_password_state.nim +++ b/src/app/modules/startup/internal/user_profile_confirm_password_state.nim @@ -24,6 +24,8 @@ method executePrimaryCommand*(self: UserProfileConfirmPasswordState, controller: elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys: controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true) elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase: - controller.storeImportedAccountAndLogin(storeToKeychain = false) + controller.storeImportedAccountAndLogin(storeToKeychain) + elif self.flowType == FlowType.LostKeycardConvertToRegularAccount: + controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain) diff --git a/src/app/modules/startup/internal/user_profile_enter_seed_phrase_state.nim b/src/app/modules/startup/internal/user_profile_enter_seed_phrase_state.nim index a85da394c1..bd72e3a6a3 100644 --- a/src/app/modules/startup/internal/user_profile_enter_seed_phrase_state.nim +++ b/src/app/modules/startup/internal/user_profile_enter_seed_phrase_state.nim @@ -32,6 +32,14 @@ method getNextPrimaryState*(self: UserProfileEnterSeedPhraseState, controller: C if self.enteredMnemonicMatchTargetedKeyUid: return createState(StateType.KeycardCreatePin, self.flowType, self) return createState(StateType.KeycardWrongKeycard, self.flowType, self) + if self.flowType == FlowType.LostKeycardReplacement: + if self.enteredMnemonicMatchTargetedKeyUid: + return createState(StateType.KeycardCreatePin, self.flowType, self) + return createState(StateType.UserProfileWrongSeedPhrase, self.flowType, self) + if self.flowType == FlowType.LostKeycardConvertToRegularAccount: + if self.enteredMnemonicMatchTargetedKeyUid: + return createState(StateType.UserProfileCreatePassword, self.flowType, self) + return createState(StateType.UserProfileWrongSeedPhrase, self.flowType, self) method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: Controller) = if self.flowType == FlowType.FirstRunNewUserImportSeedPhrase or @@ -59,6 +67,10 @@ method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase()) else: controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true)) + if self.flowType == FlowType.LostKeycardConvertToRegularAccount: + self.enteredMnemonicMatchTargetedKeyUid = controller.keyUidMatchSelectedLoginAccount(keyUid) + if not self.enteredMnemonicMatchTargetedKeyUid: + controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true)) method resolveKeycardNextState*(self: UserProfileEnterSeedPhraseState, keycardFlowType: string, keycardEvent: KeycardEvent, controller: Controller): State = diff --git a/src/app/modules/startup/internal/user_profile_wrong_seed_phrase_state.nim b/src/app/modules/startup/internal/user_profile_wrong_seed_phrase_state.nim new file mode 100644 index 0000000000..41476a9da5 --- /dev/null +++ b/src/app/modules/startup/internal/user_profile_wrong_seed_phrase_state.nim @@ -0,0 +1,22 @@ +type + UserProfileWrongSeedPhraseState* = ref object of State + +proc newUserProfileWrongSeedPhraseState*(flowType: FlowType, backState: State): UserProfileWrongSeedPhraseState = + result = UserProfileWrongSeedPhraseState() + result.setup(flowType, StateType.UserProfileWrongSeedPhrase, backState) + +proc delete*(self: UserProfileWrongSeedPhraseState) = + self.State.delete + +method executeBackCommand*(self: UserProfileWrongSeedPhraseState, controller: Controller) = + if self.flowType == FlowType.LostKeycardReplacement or + self.flowType == FlowType.LostKeycardConvertToRegularAccount: + controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false)) + +method executePrimaryCommand*(self: UserProfileWrongSeedPhraseState, controller: Controller) = + self.executeBackCommand(controller) + +method getNextPrimaryState*(self: UserProfileWrongSeedPhraseState, controller: Controller): State = + if self.flowType == FlowType.LostKeycardReplacement or + self.flowType == FlowType.LostKeycardConvertToRegularAccount: + return self.getBackState() \ No newline at end of file diff --git a/src/app/modules/startup/io_interface.nim b/src/app/modules/startup/io_interface.nim index a89d548a82..de6bb0146f 100644 --- a/src/app/modules/startup/io_interface.nim +++ b/src/app/modules/startup/io_interface.nim @@ -5,6 +5,13 @@ from ../../../app_service/service/keycard/service import KeycardEvent, KeyDetail const UNIQUE_STARTUP_MODULE_IDENTIFIER* = "SartupModule" +type + StartupErrorType* {.pure.} = enum + UnknownType = 0 + ImportAccError + SetupAccError + ConvertToRegularAccError + type AccessInterface* {.pure inheritable.} = ref object of RootObj @@ -92,15 +99,12 @@ method getPin*(self: AccessInterface): string {.base.} = method getPasswordStrengthScore*(self: AccessInterface, password: string, userName: string): int {.base.} = raise newException(ValueError, "No implementation available") -method setupAccountError*(self: AccessInterface, error: string) {.base.} = +method emitStartupError*(self: AccessInterface, error: string, errType: StartupErrorType) {.base.} = raise newException(ValueError, "No implementation available") method validMnemonic*(self: AccessInterface, mnemonic: string): bool {.base.} = raise newException(ValueError, "No implementation available") -method importAccountError*(self: AccessInterface, error: string) {.base.} = - raise newException(ValueError, "No implementation available") - method importAccountSuccess*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/startup/module.nim b/src/app/modules/startup/module.nim index dd0311a582..25db87638d 100644 --- a/src/app/modules/startup/module.nim +++ b/src/app/modules/startup/module.nim @@ -262,15 +262,12 @@ method getPin*[T](self: Module[T]): string = method getPasswordStrengthScore*[T](self: Module[T], password, userName: string): int = return self.controller.getPasswordStrengthScore(password, userName) -method setupAccountError*[T](self: Module[T], error: string) = - self.view.setupAccountError(error) +method emitStartupError*[T](self: Module[T], error: string, errType: StartupErrorType) = + self.view.emitStartupError(error, errType) method validMnemonic*[T](self: Module[T], mnemonic: string): bool = return self.controller.validMnemonic(mnemonic) -method importAccountError*[T](self: Module[T], error: string) = - self.view.importAccountError(error) - method importAccountSuccess*[T](self: Module[T]) = self.view.importAccountSuccess() @@ -358,8 +355,15 @@ method startAppAfterDelay*[T](self: Module[T]) = self.view.setCurrentStartupState(newProfileFetchingState(currStateObj.flowType(), nil)) self.moveToStartupState() -proc logoutAndDisplayError[T](self: Module[T], error: string) = +proc logoutAndDisplayError[T](self: Module[T], error: string, errType: StartupErrorType) = self.delegate.logout() + if self.controller.isSelectedLoginAccountKeycardAccount() and + errType == StartupErrorType.ConvertToRegularAccError: + self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil)) + self.controller.runLoginFlow() + self.moveToStartupState() + self.emitStartupError(error, errType) + return self.moveToStartupState() self.emitAccountLoginError(error) @@ -377,12 +381,20 @@ method onNodeLogin*[T](self: Module[T], error: string) = self.delayStartingApp() let err = self.delegate.userLoggedIn() if err.len > 0: - self.logoutAndDisplayError(err) + self.logoutAndDisplayError(err, StartupErrorType.UnknownType) return + elif currStateObj.flowType() == FlowType.LostKeycardConvertToRegularAccount: + let err = self.controller.convertToRegularAccount() + if err.len > 0: + self.logoutAndDisplayError(err, StartupErrorType.ConvertToRegularAccError) + return + self.delegate.logout() + self.view.setCurrentStartupState(newLoginKeycardConvertedToRegularAccountState(currStateObj.flowType(), nil)) + self.moveToStartupState() else: let err = self.delegate.userLoggedIn() if err.len > 0: - self.logoutAndDisplayError(err) + self.logoutAndDisplayError(err, StartupErrorType.UnknownType) return self.delegate.finishAppLoading() if currStateObj.flowType() != FlowType.AppLogin: @@ -393,7 +405,7 @@ method onNodeLogin*[T](self: Module[T], error: string) = if currStateObj.flowType() == FlowType.AppLogin: self.emitAccountLoginError(error) else: - self.setupAccountError(error) + self.emitStartupError(error, StartupErrorType.SetupAccError) error "login error", methodName="onNodeLogin", errDesription =error method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEvent: KeycardEvent) = diff --git a/src/app/modules/startup/view.nim b/src/app/modules/startup/view.nim index 7df6a61edd..f898e6aabe 100644 --- a/src/app/modules/startup/view.nim +++ b/src/app/modules/startup/view.nim @@ -192,18 +192,13 @@ QtObject: proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} = return self.delegate.getPasswordStrengthScore(password, userName) - proc accountSetupError*(self: View, error: string) {.signal.} - proc setupAccountError*(self: View, error: string) = - self.accountSetupError(error) + proc startupError*(self: View, error: string, errType: int) {.signal.} + proc emitStartupError*(self: View, error: string, errType: StartupErrorType) = + self.startupError(error, errType.int) proc validMnemonic*(self: View, mnemonic: string): bool {.slot.} = return self.delegate.validMnemonic(mnemonic) - proc accountImportError*(self: View, error: string) {.signal.} - proc importAccountError*(self: View, error: string) = - # In QML we can connect to this signal and notify user, before refactoring we didn't have this signal - self.accountImportError(error) - proc accountImportSuccess*(self: View) {.signal.} proc importAccountSuccess*(self: View) = self.importedAccountChanged() diff --git a/src/app_service/service/accounts/async_tasks.nim b/src/app_service/service/accounts/async_tasks.nim index b8d399b66c..e4e29d30d5 100644 --- a/src/app_service/service/accounts/async_tasks.nim +++ b/src/app_service/service/accounts/async_tasks.nim @@ -8,12 +8,11 @@ type settingsJson: JsonNode hashedCurrentPassword: string newPassword: string - keyStoreDir: string const convertToKeycardAccountTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = let arg = decode[ConvertToKeycardAccountTaskArg](argEncoded) try: - let response = status_account.convertToKeycardAccount(arg.keyStoreDir, arg.accountDataJson, arg.settingsJson, + let response = status_account.convertToKeycardAccount(arg.accountDataJson, arg.settingsJson, arg.hashedCurrentPassword, arg.newPassword) arg.finish(response) except Exception as e: diff --git a/src/app_service/service/accounts/service.nim b/src/app_service/service/accounts/service.nim index 937865a5fc..c9096599d6 100644 --- a/src/app_service/service/accounts/service.nim +++ b/src/app_service/service/accounts/service.nim @@ -462,7 +462,7 @@ QtObject: "public-key": whisperPublicKey, "name": alias, "display-name": displayName, - "address": whisperAddress, + "address": address, "eip1581-address": eip1581Address, "dapps-address": walletAddress, "wallet-root-address": walletRootAddress, @@ -631,21 +631,15 @@ QtObject: error "error: ", procName="login", errName = e.name, errDesription = e.msg return e.msg - proc loginAccountKeycard*(self: Service, keycardData: KeycardEvent): string = + proc loginAccountKeycard*(self: Service, accToBeLoggedIn: AccountDto, keycardData: KeycardEvent): string = try: self.setKeyStoreDir(keycardData.keyUid) - let openedAccounts = self.openedAccounts() - var accToBeLoggedIn: AccountDto - for acc in openedAccounts: - if acc.keyUid == keycardData.keyUid: - accToBeLoggedIn = acc - break - var accountDataJson = %* { "name": accToBeLoggedIn.name, "address": keycardData.masterKey.address, - "key-uid": keycardData.keyUid + "key-uid": keycardData.keyUid, + "kdfIterations": KDF_ITERATIONS, } var settingsJson: JsonNode self.addKeycardDetails(settingsJson, accountDataJson) @@ -681,14 +675,12 @@ QtObject: error "error: ", procName="verifyAccountPassword", errName = e.name, errDesription = e.msg - proc convertToKeycardAccount*(self: Service, keyUid: string, currentPassword: string, newPassword: string) = + proc convertToKeycardAccount*(self: Service, currentPassword: string, newPassword: string) = var accountDataJson = %* { - "name": self.getLoggedInAccount().name, - "key-uid": keyUid - } - var settingsJson = %* { - "display-name": self.getLoggedInAccount().name + "key-uid": self.getLoggedInAccount().keyUid, + "kdfIterations": KDF_ITERATIONS } + var settingsJson = %* { } self.addKeycardDetails(settingsJson, accountDataJson) @@ -704,7 +696,6 @@ QtObject: slot: "onConvertToKeycardAccount", accountDataJson: accountDataJson, settingsJson: settingsJson, - keyStoreDir: self.keyStoreDir, hashedCurrentPassword: hashedCurrentPassword, newPassword: newPassword ) @@ -726,6 +717,20 @@ QtObject: error "error handilng migrated keypair response", errDesription=e.msg self.events.emit(SIGNAL_CONVERTING_PROFILE_KEYPAIR, ResultArgs(success: result)) + proc convertToRegularAccount*(self: Service, mnemonic: string, currentPassword: string, newPassword: string): string = + let hashedPassword = hashString(newPassword) + try: + let response = status_account.convertToRegularAccount(mnemonic, currentPassword, hashedPassword) + var errMsg = "" + if(response.result.contains("error")): + errMsg = response.result["error"].getStr + if errMsg.len > 0: + error "error: ", procName="convertToRegularAccount", errDesription = errMsg + return errMsg + except Exception as e: + error "error converting to regular account: ", message = e.msg + return e.msg + proc verifyPassword*(self: Service, password: string): bool = try: let hashedPassword = hashString(password) diff --git a/src/backend/accounts.nim b/src/backend/accounts.nim index b28cbe94b9..aa3e68fb7b 100644 --- a/src/backend/accounts.nim +++ b/src/backend/accounts.nim @@ -243,16 +243,25 @@ proc saveAccountAndLoginWithKeycard*(chatKey, password: string, account, subacco error "error doing rpc request", methodName = "saveAccountAndLogin", exception=e.msg raise newException(RpcException, e.msg) -proc convertToKeycardAccount*(keyStoreDir: string, account: JsonNode, settings: JsonNode, password: string, newPassword: string): +proc convertToKeycardAccount*(account: JsonNode, settings: JsonNode, password: string, newPassword: string): RpcResponse[JsonNode] {.raises: [Exception].} = try: - let response = status_go.convertToKeycardAccount(keyStoreDir, $account, $settings, password, newPassword) + let response = status_go.convertToKeycardAccount($account, $settings, password, newPassword) result.result = Json.decode(response, JsonNode) except RpcException as e: error "error doing rpc request", methodName = "convertToKeycardAccount", exception=e.msg raise newException(RpcException, e.msg) +proc convertToRegularAccount*(mnemonic: string, currPassword: string, newPassword: string): + RpcResponse[JsonNode] {.raises: [Exception].} = + try: + let response = status_go.convertToRegularAccount(mnemonic, currPassword, newPassword) + result.result = Json.decode(response, JsonNode) + except RpcException as e: + error "error doing rpc request", methodName = "convertToRegularAccount", exception=e.msg + raise newException(RpcException, e.msg) + proc login*(name, keyUid: string, kdfIterations: int, hashedPassword, thumbnail, large: string, nodeCfgObj: string): RpcResponse[JsonNode] {.raises: [Exception].} = diff --git a/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml b/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml index d4d5d131d9..f03ac2c90d 100644 --- a/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml +++ b/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml @@ -2,6 +2,10 @@ import QtQuick 2.14 import QtQuick.Controls 2.14 import QtQuick.Dialogs 1.3 +import StatusQ.Core 0.1 +import StatusQ.Popups.Dialog 0.1 +import StatusQ.Core.Theme 0.1 + import utils 1.0 import shared.popups.keycard 1.0 @@ -77,6 +81,7 @@ OnboardingBasePage { case Constants.startupState.loginKeycardMaxPairingSlotsReached: case Constants.startupState.loginKeycardEmpty: case Constants.startupState.loginNotKeycard: + case Constants.startupState.loginKeycardConvertedToRegularAccount: return loginViewComponent case Constants.startupState.keycardPluginReader: @@ -108,6 +113,7 @@ OnboardingBasePage { case Constants.startupState.keycardMaxPairingSlotsReached: case Constants.startupState.keycardMaxPinRetriesReached: case Constants.startupState.keycardMaxPukRetriesReached: + case Constants.startupState.userProfileWrongSeedPhrase: return keycardStateViewComponent case Constants.startupState.keycardEnterPuk: @@ -132,29 +138,40 @@ OnboardingBasePage { Connections { target: root.startupStore.startupModuleInst - onAccountSetupError: { - if (error === Constants.existingAccountError) { - msgDialog.title = qsTr("Keys for this account already exist") - msgDialog.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase") - } else { - msgDialog.title = qsTr("Login failed") - msgDialog.text = qsTr("Login failed. Please re-enter your password and try again.") + onStartupError: { + msgDialog.errType = errType + if (errType === Constants.startupErrorType.setupAccError) { + if (error === Constants.existingAccountError) { + msgDialog.title = qsTr("Keys for this account already exist") + msgDialog.text = qsTr("Keys for this account already exist and can't be added again. If you've lost\ +your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase.") + } else { + msgDialog.title = qsTr("Login failed") + msgDialog.text = qsTr("Login failed. Please re-enter your password and try again.") + } } + else if (errType === Constants.startupErrorType.importAccError) { + if (error === Constants.existingAccountError) { + msgDialog.title = qsTr("Keys for this account already exist") + msgDialog.text = qsTr("Keys for this account already exist and can't be added again. If you've lost\ +your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase. In\ +case of Keycard try recovering using PUK or reinstall the app and try login with the Keycard option.") + } else { + msgDialog.title = qsTr("Error importing seed") + msgDialog.text = error + } + } + else if (errType === Constants.startupErrorType.convertToRegularAccError) { + msgDialog.title = qsTr("Converting account") + msgDialog.text = qsTr("Really sorry about this inconvenience.\n\ +Most likely that your account is damaged while converting to a regular Status account.\n\ +First try to login after app restart, if that doesn't work, you can alway recover your accout\n\ +following the \"Add existing Status user\" flow, using your seed phrase.") + } + msgDialog.open() } - onAccountImportError: { - if (error === Constants.existingAccountError) { - msgDialog.title = qsTr("Keys for this account already exist") - msgDialog.text = qsTr("Keys for this account already exist and can't be added again. If you've lost \ -your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase. In \ -case of Keycard try recovering using PUK or reinstall the app and try login with the Keycard option.") - } else { - msgDialog.title = qsTr("Error importing seed") - msgDialog.text = error - } - msgDialog.open() - } onDisplayKeycardSharedModuleFlow: { keycardPopup.active = true } @@ -163,13 +180,26 @@ case of Keycard try recovering using PUK or reinstall the app and try login with } } - MessageDialog { + StatusDialog { id: msgDialog title: qsTr("Login failed") - text: qsTr("Login failed. Please re-enter your password and try again.") - icon: StandardIcon.Critical - standardButtons: StandardButton.Ok + + property int errType: Constants.startupErrorType.unknownType + property string text: qsTr("Login failed. Please re-enter your password and try again.") + + StatusBaseText { + anchors.fill: parent + font.pixelSize: 15 + color: Theme.palette.directColor1 + text: msgDialog.text + wrapMode: Text.WordWrap + } + + standardButtons: Dialog.Ok onAccepted: { + if (msgDialog.errType == Constants.startupErrorType.convertToRegularAccError) { + Qt.quit(); + } console.log("TODO: restart flow...") } } diff --git a/ui/app/AppLayouts/Onboarding/views/KeycardStateView.qml b/ui/app/AppLayouts/Onboarding/views/KeycardStateView.qml index 1aa361958e..517dfba29e 100644 --- a/ui/app/AppLayouts/Onboarding/views/KeycardStateView.qml +++ b/ui/app/AppLayouts/Onboarding/views/KeycardStateView.qml @@ -365,6 +365,44 @@ Item { color: Theme.palette.baseColor1 font.pixelSize: Constants.keycard.general.fontSize3 } + }, + State { + name: Constants.startupState.userProfileWrongSeedPhrase + when: root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileWrongSeedPhrase + PropertyChanges { + target: image + pattern: Constants.keycardAnimations.strongError.pattern + source: "" + startImgIndexForTheFirstLoop: Constants.keycardAnimations.strongError.startImgIndexForTheFirstLoop + startImgIndexForOtherLoops: Constants.keycardAnimations.strongError.startImgIndexForOtherLoops + endImgIndex: Constants.keycardAnimations.strongError.endImgIndex + duration: Constants.keycardAnimations.strongError.duration + loops: Constants.keycardAnimations.strongError.loops + } + PropertyChanges { + target: title + text: qsTr("Seed phrase doesn’t match any user") + color: Theme.palette.directColor1 + font.pixelSize: Constants.keycard.general.fontSize1 + } + PropertyChanges { + target: info + text: qsTr("The seed phrase you enter needs to match the seed phrase of an existing user on this device") + color: Theme.palette.directColor1 + font.pixelSize: Constants.keycard.general.fontSize2 + } + PropertyChanges { + target: button + text: qsTr("Try entering seed phrase again") + } + PropertyChanges { + target: link + text: "" + } + PropertyChanges { + target: message + text: "" + } } ] } diff --git a/ui/app/AppLayouts/Onboarding/views/LoginView.qml b/ui/app/AppLayouts/Onboarding/views/LoginView.qml index 3b9f8a55df..47c2411189 100644 --- a/ui/app/AppLayouts/Onboarding/views/LoginView.qml +++ b/ui/app/AppLayouts/Onboarding/views/LoginView.qml @@ -1265,6 +1265,56 @@ Item { text: "" visible: false } + }, + State { + name: Constants.startupState.loginKeycardConvertedToRegularAccount + when: root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardConvertedToRegularAccount + PropertyChanges { + target: image + pattern: Constants.keycardAnimations.strongSuccess.pattern + source: "" + startImgIndexForTheFirstLoop: Constants.keycardAnimations.strongSuccess.startImgIndexForTheFirstLoop + startImgIndexForOtherLoops: Constants.keycardAnimations.strongSuccess.startImgIndexForOtherLoops + endImgIndex: Constants.keycardAnimations.strongSuccess.endImgIndex + duration: Constants.keycardAnimations.strongSuccess.duration + loops: Constants.keycardAnimations.strongSuccess.loops + } + PropertyChanges { + target: userInfo + visible: false + } + PropertyChanges { + target: title + text: qsTr("Your account has been successfully converted to a non Keycard account") + visible: true + } + PropertyChanges { + target: passwordSection + visible: false + } + PropertyChanges { + target: pinSection + visible: false + } + PropertyChanges { + target: info + visible: false + } + PropertyChanges { + target: message + text: qsTr("To complete the process close Status and log in with your password") + visible: true + } + PropertyChanges { + target: button + text: qsTr("Restart app & sign in using your password") + visible: true + } + PropertyChanges { + target: link + text: "" + visible: false + } } ] } diff --git a/ui/app/AppLayouts/Onboarding/views/SeedPhraseInputView.qml b/ui/app/AppLayouts/Onboarding/views/SeedPhraseInputView.qml index 80b43a25c9..1b35992031 100644 --- a/ui/app/AppLayouts/Onboarding/views/SeedPhraseInputView.qml +++ b/ui/app/AppLayouts/Onboarding/views/SeedPhraseInputView.qml @@ -316,7 +316,8 @@ Item { return qsTr("Recover Keycard") } else if (root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhraseIntoKeycard || - root.startupStore.currentStartupState.flowType === Constants.startupFlow.lostKeycardReplacement) { + root.startupStore.currentStartupState.flowType === Constants.startupFlow.lostKeycardReplacement || + root.startupStore.currentStartupState.flowType === Constants.startupFlow.lostKeycardConvertToRegularAccount) { return qsTr("Next") } return "" diff --git a/ui/app/AppLayouts/Profile/views/KeycardView.qml b/ui/app/AppLayouts/Profile/views/KeycardView.qml index fb256d90d9..1a9baa6bfc 100644 --- a/ui/app/AppLayouts/Profile/views/KeycardView.qml +++ b/ui/app/AppLayouts/Profile/views/KeycardView.qml @@ -6,6 +6,7 @@ import QtQml.Models 2.14 import StatusQ.Core 0.1 import StatusQ.Controls 0.1 import StatusQ.Popups.Dialog 0.1 +import StatusQ.Core.Theme 0.1 import utils 1.0 import shared.popups.keycard 1.0 diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index dd5d2ef85d..94793526dd 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -24,6 +24,7 @@ QtObject { readonly property string firstRunOldUserImportSeedPhrase: "FirstRunOldUserImportSeedPhrase" readonly property string appLogin: "AppLogin" readonly property string lostKeycardReplacement: "LostKeycardReplacement" + readonly property string lostKeycardConvertToRegularAccount: "LostKeycardConvertToRegularAccount" } readonly property QtObject startupState: QtObject { @@ -40,6 +41,7 @@ QtObject { readonly property string userProfileConfirmPassword: "UserProfileConfirmPassword" readonly property string userProfileImportSeedPhrase: "UserProfileImportSeedPhrase" readonly property string userProfileEnterSeedPhrase: "UserProfileEnterSeedPhrase" + readonly property string userProfileWrongSeedPhrase: "UserProfileWrongSeedPhrase" readonly property string biometrics: "Biometrics" readonly property string keycardPluginReader: "KeycardPluginReader" readonly property string keycardInsertKeycard: "KeycardInsertKeycard" @@ -80,6 +82,7 @@ QtObject { readonly property string loginKeycardMaxPairingSlotsReached: "LoginKeycardMaxPairingSlotsReached" readonly property string loginKeycardEmpty: "LoginKeycardEmpty" readonly property string loginNotKeycard: "LoginNotKeycard" + readonly property string loginKeycardConvertedToRegularAccount: "LoginKeycardConvertedToRegularAccount" readonly property string profileFetching: "ProfileFetching" readonly property string profileFetchingSuccess: "ProfileFetchingSuccess" readonly property string profileFetchingTimeout: "ProfileFetchingTimeout" @@ -731,6 +734,13 @@ QtObject { readonly property bool isCppApp: typeof cppApp !== "undefined" ? cppApp : false + readonly property QtObject startupErrorType: QtObject { + readonly property int unknownType: 0 + readonly property int importAccError: 1 + readonly property int setupAccError: 2 + readonly property int convertToRegularAccError: 3 + } + readonly property string existingAccountError: "account already exists" readonly property string wrongDerivationPathError: "error parsing derivation path" diff --git a/vendor/nim-status-go b/vendor/nim-status-go index 1d8c32e855..8fed87fe27 160000 --- a/vendor/nim-status-go +++ b/vendor/nim-status-go @@ -1 +1 @@ -Subproject commit 1d8c32e8557a55c86c35e5d6eaccb321995851a3 +Subproject commit 8fed87fe272c4b49008cd7cc5950a96cd0751966