Skip to content

Commit

Permalink
Merge branch 'feat/rust-key-derivation' into feat/dart-key-derivation
Browse files Browse the repository at this point in the history
  • Loading branch information
dtscalac committed Nov 7, 2024
2 parents 04a3bac + 990d029 commit 4c6d8b8
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:async';

import 'package:catalyst_voices/pages/registration/recover/bloc_recover_builder.dart';
import 'package:catalyst_voices/pages/registration/widgets/wallet_connection_status.dart';
import 'package:catalyst_voices/pages/registration/widgets/wallet_summary.dart';
Expand Down Expand Up @@ -33,31 +31,20 @@ class AccountDetailsPanel extends StatelessWidget {
style: theme.textTheme.titleMedium?.copyWith(color: textColor),
),
const SizedBox(height: 24),
Expanded(
const Expanded(
child: SingleChildScrollView(
child: _BlocAccountSummery(
onRetry: () => unawaited(_retryAccountRestore(context)),
),
child: _BlocAccountSummery(),
),
),
const SizedBox(height: 24),
const _BlocNavigation(),
],
);
}

Future<void> _retryAccountRestore(BuildContext context) async {
final recover = RegistrationCubit.of(context).recover;
await recover.recoverAccount();
}
}

class _BlocAccountSummery extends StatelessWidget {
final VoidCallback? onRetry;

const _BlocAccountSummery({
this.onRetry,
});
const _BlocAccountSummery();

@override
Widget build(BuildContext context) {
Expand All @@ -71,10 +58,7 @@ class _BlocAccountSummery extends StatelessWidget {
walletSummary: value.walletSummary,
),
Failure<AccountSummaryData, LocalizedException>(:final value) =>
_RecoverAccountFailure(
exception: value,
onRetry: onRetry,
),
_RecoverAccountFailure(exception: value),
_ => const Center(child: VoicesCircularProgressIndicator()),
};
},
Expand Down Expand Up @@ -117,18 +101,19 @@ class _RecoveredAccountSummary extends StatelessWidget {

class _RecoverAccountFailure extends StatelessWidget {
final LocalizedException exception;
final VoidCallback? onRetry;

const _RecoverAccountFailure({
required this.exception,
this.onRetry,
});

@override
Widget build(BuildContext context) {
return VoicesErrorIndicator(
message: exception.message(context),
onRetry: onRetry,
onRetry: () async {
final recover = RegistrationCubit.of(context).recover;
await recover.recoverAccount();
},
);
}
}
Expand Down Expand Up @@ -185,8 +170,12 @@ class _Navigation extends StatelessWidget {
),
const SizedBox(height: 10),
VoicesTextButton(
onTap: () => RegistrationCubit.of(context).previousStep(),
child: Text(context.l10n.back),
onTap: () async {
final cubit = RegistrationCubit.of(context);
await cubit.recover.reset();
cubit.previousStep();
},
child: Text(context.l10n.recoverDifferentKeychain),
),
],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class _UnlockPasswordPanelState extends State<UnlockPasswordPanel> {
),
const SizedBox(height: 22),
_BlocNavigation(
onNextTap: _createKeychain,
onBackTap: _clearPasswordAndGoBack,
),
],
Expand All @@ -74,6 +75,16 @@ class _UnlockPasswordPanelState extends State<UnlockPasswordPanel> {
RegistrationCubit.of(context).recover.setConfirmPassword(confirmPassword);
}

Future<void> _createKeychain() async {
final cubit = RegistrationCubit.of(context);

final success = await cubit.recover.createKeychain();

if (success) {
cubit.nextStep();
}
}

void _clearPasswordAndGoBack() {
final registration = RegistrationCubit.of(context);

Expand Down Expand Up @@ -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,
});

Expand All @@ -138,6 +151,7 @@ class _BlocNavigation extends StatelessWidget {
builder: (context, state) {
return RegistrationBackNextNavigation(
isNextEnabled: state,
onNextTap: onNextTap,
onBackTap: onBackTap,
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ abstract interface class RecoverManager implements UnlockPasswordManager {

void setSeedPhraseWords(List<SeedPhraseWord> words);

Future<void> recoverAccount();
Future<bool> recoverAccount();

Future<bool> createKeychain();

Future<void> reset();
}

final class RecoverCubit extends Cubit<RecoverStateData>
Expand All @@ -27,6 +31,7 @@ final class RecoverCubit extends Cubit<RecoverStateData>
final RegistrationService _registrationService;

SeedPhrase? _seedPhrase;
Account? _recoveredAccount;

RecoverCubit({
required UserService userService,
Expand Down Expand Up @@ -66,24 +71,23 @@ final class RecoverCubit extends Cubit<RecoverStateData>
}

@override
Future<void> recoverAccount() async {
Future<bool> 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;

await _userService.useAccount(account);

final walletInfo = account.walletInfo;
Expand All @@ -102,23 +106,69 @@ final class RecoverCubit extends Cubit<RecoverStateData>
);

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<bool> 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
void onUnlockPasswordStateChanged(UnlockPasswordState data) {
emit(state.copyWith(unlockPasswordState: data));
}

@override
Future<void> reset() async {
final recoveredAccount = _recoveredAccount;
if (recoveredAccount != null) {
await _userService.removeKeychain(recoveredAccount.keychainId);
}

_recoveredAccount = null;

setSeedPhraseWords([]);
}
}

const _testWords = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ final class SessionCubit extends Cubit<SessionState>
}

Future<void> removeKeychain() {
return _userService.removeCurrentAccount();
return _userService.removeCurrentKeychain();
}

Future<void> switchToDummyAccount() async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void main() {
// Given

// When
await userService.removeCurrentAccount();
await userService.removeCurrentKeychain();

// Then
expect(userService.keychain, isNull);
Expand All @@ -54,7 +54,7 @@ void main() {
// Given

// When
await userService.removeCurrentAccount();
await userService.removeCurrentKeychain();

// Gives time for stream to emit.
await Future<void>.delayed(const Duration(milliseconds: 100));
Expand All @@ -80,7 +80,7 @@ void main() {
// When
notifier.value = RegistrationProgress(keychainProgress: keychainProgress);

await userService.removeCurrentAccount();
await userService.removeCurrentKeychain();

// Gives time for stream to emit.
await Future<void>.delayed(const Duration(milliseconds: 100));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1738,6 +1738,12 @@ abstract class VoicesLocalizations {
/// **'With over 300 trillion possible combinations, your 12 word seed phrase is great for keeping your account safe. 

But it can be a bit tedious to enter every single time you want to use the app. 

In this next step, you\'ll set your Unlock Password for your current device. It\'s like a shortcut for proving ownership of your Keychain. 

Whenever you recover your account for the first time on a new device, you\'ll need to use your Catalyst Keychain to get started. Every time after that, you can use your Unlock Password to quickly regain access.'**
String get recoveryUnlockPasswordInstructionsSubtitle;

/// No description provided for @recoverDifferentKeychain.
///
/// In en, this message translates to:
/// **'Restore a different keychain'**
String get recoverDifferentKeychain;

/// The header label in unlock dialog.
///
/// In en, this message translates to:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,9 @@ class VoicesLocalizationsEn extends VoicesLocalizations {
@override
String get recoveryUnlockPasswordInstructionsSubtitle => 'With over 300 trillion possible combinations, your 12 word seed phrase is great for keeping your account safe. 

But it can be a bit tedious to enter every single time you want to use the app. 

In this next step, you\'ll set your Unlock Password for your current device. It\'s like a shortcut for proving ownership of your Keychain. 

Whenever you recover your account for the first time on a new device, you\'ll need to use your Catalyst Keychain to get started. Every time after that, you can use your Unlock Password to quickly regain access.';

@override
String get recoverDifferentKeychain => 'Restore a different keychain';

@override
String get unlockDialogHeader => 'Unlock Catalyst';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,9 @@ class VoicesLocalizationsEs extends VoicesLocalizations {
@override
String get recoveryUnlockPasswordInstructionsSubtitle => 'With over 300 trillion possible combinations, your 12 word seed phrase is great for keeping your account safe. 

But it can be a bit tedious to enter every single time you want to use the app. 

In this next step, you\'ll set your Unlock Password for your current device. It\'s like a shortcut for proving ownership of your Keychain. 

Whenever you recover your account for the first time on a new device, you\'ll need to use your Catalyst Keychain to get started. Every time after that, you can use your Unlock Password to quickly regain access.';

@override
String get recoverDifferentKeychain => 'Restore a different keychain';

@override
String get unlockDialogHeader => 'Unlock Catalyst';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,7 @@
"recoveryAccountDetailsAction": "Set unlock password for this device",
"recoveryUnlockPasswordInstructionsTitle": "Set your Catalyst unlock password f\u2028or this device",
"recoveryUnlockPasswordInstructionsSubtitle": "With over 300 trillion possible combinations, your 12 word seed phrase is great for keeping your account safe. \u2028\u2028But it can be a bit tedious to enter every single time you want to use the app. \u2028\u2028In this next step, you'll set your Unlock Password for your current device. It's like a shortcut for proving ownership of your Keychain. \u2028\u2028Whenever you recover your account for the first time on a new device, you'll need to use your Catalyst Keychain to get started. Every time after that, you can use your Unlock Password to quickly regain access.",
"recoverDifferentKeychain": "Restore a different keychain",
"unlockDialogHeader": "Unlock Catalyst",
"@unlockDialogHeader": {
"description": "The header label in unlock dialog."
Expand Down
Loading

0 comments on commit 4c6d8b8

Please sign in to comment.