diff --git a/catalyst_voices/apps/voices/lib/pages/registration/recover/seed_phrase/unlock_password_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/recover/seed_phrase/unlock_password_panel.dart index 4a63389376..6e81bc7c36 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/recover/seed_phrase/unlock_password_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/recover/seed_phrase/unlock_password_panel.dart @@ -56,6 +56,7 @@ class _UnlockPasswordPanelState extends State { ), const SizedBox(height: 22), _BlocNavigation( + onNextTap: _createKeychain, onBackTap: _clearPasswordAndGoBack, ), ], @@ -74,6 +75,16 @@ class _UnlockPasswordPanelState extends State { RegistrationCubit.of(context).recover.setConfirmPassword(confirmPassword); } + Future _createKeychain() async { + final cubit = RegistrationCubit.of(context); + + final success = await cubit.recover.createKeychain(); + + if (success) { + cubit.nextStep(); + } + } + void _clearPasswordAndGoBack() { final registration = RegistrationCubit.of(context); @@ -124,9 +135,11 @@ class _BlocUnlockPasswordForm extends StatelessWidget { } class _BlocNavigation extends StatelessWidget { + final VoidCallback onNextTap; final VoidCallback onBackTap; const _BlocNavigation({ + required this.onNextTap, required this.onBackTap, }); @@ -138,6 +151,7 @@ class _BlocNavigation extends StatelessWidget { builder: (context, state) { return RegistrationBackNextNavigation( isNextEnabled: state, + onNextTap: onNextTap, onBackTap: onBackTap, ); }, diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/registration/cubits/recover_cubit.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/registration/cubits/recover_cubit.dart index abe64ca8c6..df6bbbc3c6 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/registration/cubits/recover_cubit.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/registration/cubits/recover_cubit.dart @@ -17,7 +17,9 @@ abstract interface class RecoverManager implements UnlockPasswordManager { void setSeedPhraseWords(List words); - Future recoverAccount(); + Future recoverAccount(); + + Future createKeychain(); Future reset(); } @@ -69,22 +71,19 @@ final class RecoverCubit extends Cubit } @override - Future recoverAccount() async { + Future recoverAccount() async { try { emit(state.copyWith(accountDetails: const Optional.empty())); final seedPhrase = _seedPhrase; - final lockFactor = PasswordLockFactor(password.value); - if (seedPhrase == null) { const exception = LocalizedRegistrationSeedPhraseNotFoundException(); emit(state.copyWith(accountDetails: Optional(Failure(exception)))); - return; + return false; } final account = await _registrationService.recoverAccount( seedPhrase: seedPhrase, - lockFactor: lockFactor, ); _recoveredAccount = account; @@ -107,17 +106,51 @@ final class RecoverCubit extends Cubit ); emit(state.copyWith(accountDetails: Optional(Success(accountDetails)))); + + return true; } on RegistrationException catch (error, stack) { _logger.severe('recover account', error, stack); + _recoveredAccount = null; + final exception = LocalizedRegistrationException.from(error); emit(state.copyWith(accountDetails: Optional(Failure(exception)))); + + return false; } catch (error, stack) { _logger.severe('recover account', error, stack); + _recoveredAccount = null; + const exception = LocalizedUnknownException(); emit(state.copyWith(accountDetails: Optional(Failure(exception)))); + + return false; + } + } + + @override + Future createKeychain() async { + final account = _recoveredAccount; + final seedPhrase = _seedPhrase; + final password = this.password; + + if (account == null || seedPhrase == null || password.isNotValid) { + emitError(const LocalizedRegistrationUnknownException()); + return false; } + + final lockFactor = PasswordLockFactor(password.value); + + await _registrationService.createKeychainFor( + account: account, + seedPhrase: seedPhrase, + lockFactor: lockFactor, + ); + + await _userService.useAccount(account); + + return true; } @override diff --git a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/registration/registration_service.dart b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/registration/registration_service.dart index 83aaae55b8..0e1770c753 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/registration/registration_service.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/registration/registration_service.dart @@ -48,8 +48,15 @@ abstract interface class RegistrationService { required Set roles, }); + /// Loads account related to this [seedPhrase]. Throws exception if non found. Future recoverAccount({ required SeedPhrase seedPhrase, + }); + + /// Creates [Keychain] for given [account] with [lockFactor]. + Future createKeychainFor({ + required Account account, + required SeedPhrase seedPhrase, required LockFactor lockFactor, }); @@ -138,7 +145,6 @@ final class RegistrationServiceImpl implements RegistrationService { @override Future recoverAccount({ required SeedPhrase seedPhrase, - required LockFactor lockFactor, }) async { await Future.delayed(const Duration(milliseconds: 200)); @@ -148,17 +154,7 @@ final class RegistrationServiceImpl implements RegistrationService { } final roles = {AccountRole.root}; - // TODO(dtscalac): Update key value when derivation is final. - final keyPair = await deriveAccountRoleKeyPair( - seedPhrase: seedPhrase, - roles: roles, - ); - final keychainId = const Uuid().v4(); - final keychain = await _keychainProvider.create(keychainId); - await keychain.setLock(lockFactor); - await keychain.unlock(lockFactor); - await keychain.setMasterKey(keyPair.privateKey); // Note. with rootKey query backend for account details. return Account( @@ -172,6 +168,27 @@ final class RegistrationServiceImpl implements RegistrationService { ); } + @override + Future createKeychainFor({ + required Account account, + required SeedPhrase seedPhrase, + required LockFactor lockFactor, + }) async { + final keychainId = account.keychainId; + + final keyPair = await deriveAccountRoleKeyPair( + seedPhrase: seedPhrase, + roles: account.roles, + ); + + final keychain = await _keychainProvider.create(keychainId); + await keychain.setLock(lockFactor); + await keychain.unlock(lockFactor); + await keychain.setMasterKey(keyPair.privateKey); + + return keychain; + } + @override Future prepareRegistration({ required CardanoWallet wallet,