dev
dart 144 lines 3.85 KB
Raw
1 import 'dart:math';
2
3 import 'package:cake_wallet/utils/feature_flag.dart';
4 import 'package:cw_core/utils/print_verbose.dart';
5 import 'package:flutter/foundation.dart';
6 import 'package:cw_core/wallet_type.dart';
7 import 'package:mobx/mobx.dart';
8 import 'package:cw_core/wallet_base.dart';
9
10 part 'wallet_seed_view_model.g.dart';
11
12 class WalletSeedViewModel = WalletSeedViewModelBase with _$WalletSeedViewModel;
13
14 abstract class WalletSeedViewModelBase with Store {
15 WalletSeedViewModelBase(WalletBase wallet)
16 : name = wallet.name,
17 seed = wallet.seed!,
18 walletType = wallet.type,
19 currentOptions = ObservableList<String>(),
20 verificationIndices = ObservableList<int>() {
21 setupSeedVerification();
22 }
23
24 @observable
25 String name;
26
27 @observable
28 String seed;
29
30 @observable
31 WalletType walletType;
32
33 /// The Regex split the words based on any whitespace character.
34 ///
35 /// Either standard ASCII space (U+0020) or the full-width space character (U+3000) used by the Japanese.
36 List<String> get seedSplit => seed.split(RegExp(r'\s+'));
37
38 int get columnCount => seedSplit.length <= 16 ? 2 : 3;
39
40 double get columnAspectRatio => seedSplit.length <= 16 ? 1.8 : 2.8;
41
42 /// The indices of the seed to be verified.
43 ObservableList<int> verificationIndices;
44
45 /// The index of the word in verificationIndices being verified.
46 @observable
47 int currentStepIndex = 0;
48
49 /// The options to be displayed on the page for the current seed step.
50 ///
51 /// The user has to choose from these.
52 ObservableList<String> currentOptions;
53
54 /// The number of words to be verified, linked to a Feature Flag so we can easily modify it.
55 int get verificationWordCount {
56 final shouldVerify = shouldPerformVerification();
57
58 return shouldVerify ? FeatureFlag.verificationWordsCount : 0;
59 }
60
61 /// Then number of wrong entries the user has selected;
62 ///
63 /// Routes the view to the seed screen if it's up to two.
64 @observable
65 int wrongEntries = 0;
66
67 int get currentWordIndex => verificationIndices[currentStepIndex];
68
69 String get currentCorrectWord => seedSplit[currentWordIndex];
70
71 @observable
72 bool isVerificationComplete = false;
73
74 bool shouldPerformVerification() {
75 bool isCI = bool.fromEnvironment('CI_BUILD', defaultValue: false);
76 bool isDebug = kDebugMode || kProfileMode;
77
78 if (isDebug && !isCI) {
79 printV("Skipping verification in debug mode - $isDebug (and when it's not in CI - $isCI).");
80 return false;
81 }
82
83 return true;
84 }
85
86 void setupSeedVerification() {
87 if (verificationWordCount != 0) {
88 generateRandomIndices();
89 generateOptions();
90 }
91 }
92
93 /// Generate the indices of the seeds to be verified.
94 ///
95 /// Structured to be as random as possible.
96 @action
97 void generateRandomIndices() {
98 verificationIndices.clear();
99 final random = Random();
100 final indices = <int>[];
101 while (indices.length < verificationWordCount) {
102 final i = random.nextInt(seedSplit.length);
103 if (!indices.contains(i)) {
104 indices.add(i);
105 }
106 }
107
108 verificationIndices.addAll(indices);
109 }
110
111 /// Generates the options for each index being verified.
112 @action
113 void generateOptions() {
114 currentOptions.clear();
115
116 final correctWord = currentCorrectWord;
117 final incorrectWords = seedSplit.where((word) => word != correctWord).toList();
118 incorrectWords.shuffle();
119
120 final options = [correctWord, ...incorrectWords.take(5)];
121 options.shuffle();
122
123 currentOptions.addAll(options);
124 }
125
126 bool isChosenWordCorrect(String chosenWord) {
127 if (chosenWord == currentCorrectWord) {
128 wrongEntries = 0;
129
130 if (currentStepIndex + 1 < verificationWordCount) {
131 currentStepIndex++;
132 generateOptions();
133 } else {
134 // All verification steps completed
135 isVerificationComplete = true;
136 }
137
138 return true;
139 } else {
140 wrongEntries++;
141 return false;
142 }
143 }
144 }