Backup stuff.

M committed Jan 15, 2021 at 19:41 UTC 47ceac2dd6101a1071c2afb5486f03747eb1351e
24 files changed +417 -211
lib/core/backup_service.dart renamed
+124 -67
@@ -14,20 +14,93 @@ import 'package:cake_wallet/entities/encrypt.dart';
14 import 'package:cake_wallet/entities/preferences_key.dart';
15 import 'package:cake_wallet/entities/secret_store_key.dart';
16 import 'package:cake_wallet/entities/wallet_info.dart';
17 +import 'package:cake_wallet/.secrets.g.dart' as secrets;
18
19 class BackupService {
19 - BackupService(this._flutterSecureStorage, this._authService,
20 - this._walletInfoSource, this._keyService, this._sharedPreferences)
20 + BackupService(this._flutterSecureStorage, this._walletInfoSource,
21 + this._keyService, this._sharedPreferences)
22 : _cipher = chacha20Poly1305Aead;
23
24 + static const currentVersion = _v1;
25 +
26 + static const _v1 = 1;
27 +
28 final Cipher _cipher;
29 final FlutterSecureStorage _flutterSecureStorage;
30 final SharedPreferences _sharedPreferences;
26 - final AuthService _authService;
31 final Box<WalletInfo> _walletInfoSource;
32 final KeyService _keyService;
33
34 Future<void> importBackup(Uint8List data, String password,
35 + {String nonce = secrets.backupSalt}) async {
36 + final version = getVersion(data);
37 + final backupBytes = data.toList()..removeAt(0);
38 + final backupData = Uint8List.fromList(backupBytes);
39 +
40 + switch (version) {
41 + case _v1:
42 + await _importBackupV1(backupData, password, nonce: nonce);
43 + break;
44 + default:
45 + break;
46 + }
47 + }
48 +
49 + Future<Uint8List> exportBackup(String password,
50 + {String nonce = secrets.backupSalt, int version = currentVersion}) async {
51 + switch (version) {
52 + case _v1:
53 + return await _exportBackupV1(password, nonce: nonce);
54 + default:
55 + return null;
56 + }
57 + }
58 +
59 + Future<Uint8List> _exportBackupV1(String password,
60 + {String nonce = secrets.backupSalt}) async {
61 + final zipEncoder = ZipFileEncoder();
62 + final appDir = await getApplicationDocumentsDirectory();
63 + final now = DateTime.now();
64 + final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP');
65 + final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip';
66 + final fileEntities = appDir.listSync(recursive: false);
67 + final keychainDump = await _exportKeychainDump(password, nonce: nonce);
68 + final preferencesDump = await _exportPreferencesJSON();
69 + final preferencesDumpFile = File('${tmpDir.path}/~_preferences_dump_TMP');
70 + final keychainDumpFile = File('${tmpDir.path}/~_keychain_dump_TMP');
71 +
72 + if (tmpDir.existsSync()) {
73 + tmpDir.deleteSync(recursive: true);
74 + }
75 +
76 + tmpDir.createSync();
77 + zipEncoder.create(archivePath);
78 +
79 + fileEntities.forEach((entity) {
80 + if (entity.path == archivePath || entity.path == tmpDir.path) {
81 + return;
82 + }
83 +
84 + if (entity.statSync().type == FileSystemEntityType.directory) {
85 + zipEncoder.addDirectory(Directory(entity.path));
86 + } else {
87 + zipEncoder.addFile(File(entity.path));
88 + }
89 + });
90 + await keychainDumpFile.writeAsBytes(keychainDump.toList());
91 + await preferencesDumpFile.writeAsString(preferencesDump);
92 + zipEncoder.addFile(preferencesDumpFile, '~_preferences_dump');
93 + zipEncoder.addFile(keychainDumpFile, '~_keychain_dump');
94 + zipEncoder.close();
95 +
96 + final content = File(archivePath).readAsBytesSync();
97 + tmpDir.deleteSync(recursive: true);
98 + final encryptedData = await _encrypt(content, password, nonce);
99 +
100 + return setVersion(encryptedData, currentVersion);
101 + }
102 +
103 + Future<void> _importBackupV1(Uint8List data, String password,
104 {@required String nonce}) async {
105 final appDir = await getApplicationDocumentsDirectory();
106 final decryptedData = await _decrypt(data, password, nonce);
@@ -37,24 +110,23 @@ class BackupService {
110 final filename = file.name;
111
112 if (file.isFile) {
40 - final data = file.content as List<int>;
113 + final content = file.content as List<int>;
114 File('${appDir.path}/' + filename)
115 ..createSync(recursive: true)
43 - ..writeAsBytesSync(data);
116 + ..writeAsBytesSync(content);
117 } else {
118 Directory('${appDir.path}/' + filename)..create(recursive: true);
119 }
47 -
48 - print(filename);
120 });
121
51 - await importKeychainDump(password, nonce: nonce);
52 - await importPreferencesDump();
122 + await _importKeychainDump(password, nonce: nonce);
123 + await _importPreferencesDump();
124 }
125
55 - Future<void> importPreferencesDump() async {
126 + Future<void> _importPreferencesDump() async {
127 final appDir = await getApplicationDocumentsDirectory();
128 final preferencesFile = File('${appDir.path}/~_preferences_dump');
129 + const defaultSettingsMigrationVersionKey = PreferencesKey.currentDefaultSettingsMigrationVersion;
130
131 if (!preferencesFile.existsSync()) {
132 return;
@@ -62,7 +134,6 @@ class BackupService {
134
135 final data =
136 json.decode(preferencesFile.readAsStringSync()) as Map<String, Object>;
65 - print('data $data');
137
138 await _sharedPreferences.setString(PreferencesKey.currentWalletName,
139 data[PreferencesKey.currentWalletName] as String);
@@ -92,21 +163,30 @@ class BackupService {
163 data[PreferencesKey.displayActionListModeKey] as int);
164 await _sharedPreferences.setInt(
165 'current_theme', data['current_theme'] as int);
166 + await _sharedPreferences.setInt(defaultSettingsMigrationVersionKey,
167 + data[defaultSettingsMigrationVersionKey] as int);
168
169 await preferencesFile.delete();
170 }
171
99 - Future<void> importKeychainDump(String password,
100 - {@required String nonce}) async {
172 + Future<void> _importKeychainDump(String password,
173 + {@required String nonce,
174 + String keychainSalt = secrets.backupKeychainSalt}) async {
175 final appDir = await getApplicationDocumentsDirectory();
176 final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
103 - final decryptedKeychainDumpFileData =
104 - await _decrypt(keychainDumpFile.readAsBytesSync(), password, nonce);
177 + final decryptedKeychainDumpFileData = await _decrypt(
178 + keychainDumpFile.readAsBytesSync(), '$keychainSalt$password', nonce);
179 final keychainJSON = json.decode(utf8.decode(decryptedKeychainDumpFileData))
180 as Map<String, dynamic>;
181 final keychainWalletsInfo = keychainJSON['wallets'] as List;
182 final decodedPin = keychainJSON['pin'] as String;
183 final pinCodeKey = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
184 + final backupPasswordKey =
185 + generateStoreKeyFor(key: SecretStoreKey.backupPassword);
186 + final backupPassword = keychainJSON[backupPasswordKey] as String;
187 +
188 + await _flutterSecureStorage.write(
189 + key: backupPasswordKey, value: backupPassword);
190
191 keychainWalletsInfo.forEach((dynamic rawInfo) async {
192 final info = rawInfo as Map<String, dynamic>;
@@ -126,51 +206,9 @@ class BackupService {
206 await _keyService.saveWalletPassword(walletName: name, password: password);
207 }
208
129 - Future<Uint8List> exportBackup(String password,
130 - {@required String nonce}) async {
131 - final zipEncoder = ZipFileEncoder();
132 - final appDir = await getApplicationDocumentsDirectory();
133 - final now = DateTime.now();
134 - final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP');
135 - final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip';
136 - final fileEntities = appDir.listSync(recursive: false);
137 - final keychainDump = await exportKeychainDump(password, nonce: nonce);
138 - final preferencesDump = await exportPreferencesJSON();
139 - final preferencesDumpFile = File('${tmpDir.path}/~_preferences_dump_TMP');
140 - final keychainDumpFile = File('${tmpDir.path}/~_keychain_dump_TMP');
141 -
142 - if (tmpDir.existsSync()) {
143 - tmpDir.deleteSync(recursive: true);
144 - }
145 -
146 - tmpDir.createSync();
147 - zipEncoder.create(archivePath);
148 -
149 - fileEntities.forEach((entity) {
150 - if (entity.path == archivePath || entity.path == tmpDir.path) {
151 - return;
152 - }
153 -
154 - if (entity.statSync().type == FileSystemEntityType.directory) {
155 - zipEncoder.addDirectory(Directory(entity.path));
156 - } else {
157 - zipEncoder.addFile(File(entity.path));
158 - }
159 - });
160 - await keychainDumpFile.writeAsBytes(keychainDump.toList());
161 - await preferencesDumpFile.writeAsString(preferencesDump);
162 - zipEncoder.addFile(preferencesDumpFile, '~_preferences_dump');
163 - zipEncoder.addFile(keychainDumpFile, '~_keychain_dump');
164 - zipEncoder.close();
165 -
166 - final content = File(archivePath).readAsBytesSync();
167 - tmpDir.deleteSync(recursive: true);
168 -
169 - return await _encrypt(content, password, nonce);
170 - }
171 -
172 - Future<Uint8List> exportKeychainDump(String password,
173 - {@required String nonce}) async {
209 + Future<Uint8List> _exportKeychainDump(String password,
210 + {@required String nonce,
211 + String keychainSalt = secrets.backupKeychainSalt}) async {
212 final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
213 final encodedPin = await _flutterSecureStorage.read(key: key);
214 final decodedPin = decodedPinCode(pin: encodedPin);
@@ -183,15 +221,25 @@ class BackupService {
221 await _keyService.getWalletPassword(walletName: walletInfo.name)
222 };
223 }));
186 -
187 - final data =
188 - utf8.encode(json.encode({'pin': decodedPin, 'wallets': wallets}));
189 - final encrypted = await _encrypt(Uint8List.fromList(data), password, nonce);
224 + final backupPasswordKey =
225 + generateStoreKeyFor(key: SecretStoreKey.backupPassword);
226 + final backupPassword =
227 + await _flutterSecureStorage.read(key: backupPasswordKey);
228 + final data = utf8.encode(json.encode({
229 + 'pin': decodedPin,
230 + 'wallets': wallets,
231 + backupPasswordKey: backupPassword
232 + }));
233 + final encrypted = await _encrypt(
234 + Uint8List.fromList(data), '$keychainSalt$password', nonce);
235
236 return encrypted;
237 }
238
194 - Future<String> exportPreferencesJSON() async {
239 + Future<String> _exportPreferencesJSON() async {
240 + const defaultSettingsMigrationVersionKey =
241 + 'current_default_settings_migration_version';
242 +
243 final preferences = <String, Object>{
244 PreferencesKey.currentWalletName:
245 _sharedPreferences.getString(PreferencesKey.currentWalletName),
@@ -219,13 +267,22 @@ class BackupService {
267 _sharedPreferences.getString(PreferencesKey.currentLanguageCode),
268 PreferencesKey.displayActionListModeKey:
269 _sharedPreferences.getInt(PreferencesKey.displayActionListModeKey),
222 - PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme)
223 - // FIX-ME: Unnamed constant.
270 + PreferencesKey.currentTheme:
271 + _sharedPreferences.getInt(PreferencesKey.currentTheme),
272 + defaultSettingsMigrationVersionKey:
273 + _sharedPreferences.getInt(defaultSettingsMigrationVersionKey)
274 };
275
276 return json.encode(preferences);
277 }
278
279 + int getVersion(Uint8List data) => data.toList().first;
280 +
281 + Uint8List setVersion(Uint8List data, int version) {
282 + final bytes = data.toList()..insert(0, version);
283 + return Uint8List.fromList(bytes);
284 + }
285 +
286 Future<Uint8List> _encrypt(
287 Uint8List data, String secretKeySource, String nonceBase64) async {
288 final secretKeyHash = await sha256.hash(utf8.encode(secretKeySource));
lib/di.dart
+51 -28
@@ -1,5 +1,5 @@
1 import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
2 -import 'package:cake_wallet/core/backup.dart';
2 +import 'package:cake_wallet/core/backup_service.dart';
3 import 'package:cake_wallet/core/wallet_service.dart';
4 import 'package:cake_wallet/entities/biometric_auth.dart';
5 import 'package:cake_wallet/entities/contact_record.dart';
@@ -105,6 +105,15 @@ import 'package:cake_wallet/exchange/exchange_template.dart';
105
106 final getIt = GetIt.instance;
107
108 +var _isSetupFinished = false;
109 +Box<WalletInfo> _walletInfoSource;
110 +Box<Node> _nodeSource;
111 +Box<Contact> _contactSource;
112 +Box<Trade> _tradesSource;
113 +Box<Template> _templates;
114 +Box<ExchangeTemplate> _exchangeTemplates;
115 +Box<TransactionDescription> _transactionDescriptionBox;
116 +
117 Future setup(
118 {Box<WalletInfo> walletInfoSource,
119 Box<Node> nodeSource,
@@ -113,12 +122,26 @@ Future setup(
122 Box<Template> templates,
123 Box<ExchangeTemplate> exchangeTemplates,
124 Box<TransactionDescription> transactionDescriptionBox}) async {
116 - getIt.registerSingletonAsync<SharedPreferences>(
117 - () => SharedPreferences.getInstance());
125 + _walletInfoSource = walletInfoSource;
126 + _nodeSource = nodeSource;
127 + _contactSource = contactSource;
128 + _tradesSource = tradesSource;
129 + _templates = templates;
130 + _exchangeTemplates = exchangeTemplates;
131 + _transactionDescriptionBox = transactionDescriptionBox;
132 +
133 + if (!_isSetupFinished) {
134 + getIt.registerSingletonAsync<SharedPreferences>(
135 + () => SharedPreferences.getInstance());
136 + }
137 +
138 + final settingsStore = await SettingsStoreBase.load(nodeSource: _nodeSource);
139
119 - final settingsStore = await SettingsStoreBase.load(nodeSource: nodeSource);
140 + if (_isSetupFinished) {
141 + return;
142 + }
143
121 - getIt.registerSingleton<Box<Node>>(nodeSource);
144 + getIt.registerFactory<Box<Node>>(() => _nodeSource);
145
146 getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage());
147 getIt.registerSingleton(AuthenticationStore());
@@ -131,14 +154,14 @@ Future setup(
154 settingsStore: getIt.get<SettingsStore>(),
155 nodeListStore: getIt.get<NodeListStore>()));
156 getIt.registerSingleton<TradesStore>(TradesStore(
134 - tradesSource: tradesSource, settingsStore: getIt.get<SettingsStore>()));
157 + tradesSource: _tradesSource, settingsStore: getIt.get<SettingsStore>()));
158 getIt.registerSingleton<TradeFilterStore>(TradeFilterStore());
159 getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore());
160 getIt.registerSingleton<FiatConversionStore>(FiatConversionStore());
161 getIt.registerSingleton<SendTemplateStore>(
139 - SendTemplateStore(templateSource: templates));
162 + SendTemplateStore(templateSource: _templates));
163 getIt.registerSingleton<ExchangeTemplateStore>(
141 - ExchangeTemplateStore(templateSource: exchangeTemplates));
164 + ExchangeTemplateStore(templateSource: _exchangeTemplates));
165
166 final secretStore =
167 await SecretStoreBase.load(getIt.get<FlutterSecureStorage>());
@@ -157,7 +180,7 @@ Future setup(
180
181 getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
182 WalletNewVM(getIt.get<AppStore>(),
160 - getIt.get<WalletCreationService>(param1: type), walletInfoSource,
183 + getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
184 type: type));
185
186 getIt
@@ -167,7 +190,7 @@ Future setup(
190 final mnemonic = args[2] as String;
191
192 return WalletRestorationFromSeedVM(getIt.get<AppStore>(),
170 - getIt.get<WalletCreationService>(param1: type), walletInfoSource,
193 + getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
194 type: type, language: language, seed: mnemonic);
195 });
196
@@ -177,7 +200,7 @@ Future setup(
200 final language = args[1] as String;
201
202 return WalletRestorationFromKeysVM(getIt.get<AppStore>(),
180 - getIt.get<WalletCreationService>(param1: type), walletInfoSource,
203 + getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
204 type: type, language: language);
205 });
206
@@ -262,7 +285,7 @@ Future setup(
285 getIt.get<AppStore>().settingsStore,
286 getIt.get<SendTemplateStore>(),
287 getIt.get<FiatConversionStore>(),
265 - transactionDescriptionBox));
288 + _transactionDescriptionBox));
289
290 getIt.registerFactory(
291 () => SendPage(sendViewModel: getIt.get<SendViewModel>()));
@@ -271,7 +294,7 @@ Future setup(
294 () => SendTemplatePage(sendViewModel: getIt.get<SendViewModel>()));
295
296 getIt.registerFactory(() => WalletListViewModel(
274 - walletInfoSource,
297 + _walletInfoSource,
298 getIt.get<AppStore>(),
299 getIt.get<KeyService>(),
300 getIt.get<WalletNewVM>(param1: WalletType.monero)));
@@ -342,10 +365,10 @@ Future setup(
365
366 getIt.registerFactoryParam<ContactViewModel, ContactRecord, void>(
367 (ContactRecord contact, _) =>
345 - ContactViewModel(contactSource, contact: contact));
368 + ContactViewModel(_contactSource, contact: contact));
369
370 getIt.registerFactory(
348 - () => ContactListViewModel(contactSource, walletInfoSource));
371 + () => ContactListViewModel(_contactSource, _walletInfoSource));
372
373 getIt.registerFactoryParam<ContactListPage, bool, void>(
374 (bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
@@ -358,27 +381,27 @@ Future setup(
381 getIt.registerFactory(() {
382 final appStore = getIt.get<AppStore>();
383 return NodeListViewModel(
361 - nodeSource, appStore.wallet, appStore.settingsStore);
384 + _nodeSource, appStore.wallet, appStore.settingsStore);
385 });
386
387 getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
388
389 getIt.registerFactory(() =>
367 - NodeCreateOrEditViewModel(nodeSource, getIt.get<AppStore>().wallet));
390 + NodeCreateOrEditViewModel(_nodeSource, getIt.get<AppStore>().wallet));
391
392 getIt.registerFactory(
393 () => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
394
395 getIt.registerFactory(() => ExchangeViewModel(
396 getIt.get<AppStore>().wallet,
374 - tradesSource,
397 + _tradesSource,
398 getIt.get<ExchangeTemplateStore>(),
399 getIt.get<TradesStore>(),
400 getIt.get<AppStore>().settingsStore));
401
402 getIt.registerFactory(() => ExchangeTradeViewModel(
403 wallet: getIt.get<AppStore>().wallet,
381 - trades: tradesSource,
404 + trades: _tradesSource,
405 tradesStore: getIt.get<TradesStore>(),
406 sendViewModel: getIt.get<SendViewModel>()));
407
@@ -393,9 +416,9 @@ Future setup(
416 getIt.registerFactory(
417 () => ExchangeTemplatePage(getIt.get<ExchangeViewModel>()));
418
396 - getIt.registerFactory(() => MoneroWalletService(walletInfoSource));
419 + getIt.registerFactory(() => MoneroWalletService(_walletInfoSource));
420
398 - getIt.registerFactory(() => BitcoinWalletService(walletInfoSource));
421 + getIt.registerFactory(() => BitcoinWalletService(_walletInfoSource));
422
423 getIt.registerFactoryParam<WalletService, WalletType, void>(
424 (WalletType param1, __) {
@@ -428,7 +451,7 @@ Future setup(
451
452 getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>(
453 (type, _) => WalletRestoreViewModel(getIt.get<AppStore>(),
431 - getIt.get<WalletCreationService>(param1: type), walletInfoSource,
454 + getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
455 type: type));
456
457 getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) =>
@@ -438,7 +461,7 @@ Future setup(
461 .registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
462 (TransactionInfo transactionInfo, _) => TransactionDetailsViewModel(
463 transactionInfo: transactionInfo,
441 - transactionDescriptionBox: transactionDescriptionBox,
464 + transactionDescriptionBox: _transactionDescriptionBox,
465 settingsStore: getIt.get<SettingsStore>()));
466
467 getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
@@ -455,12 +478,11 @@ Future setup(
478 (WalletType type, _) => PreSeedPage(type));
479
480 getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
458 - TradeDetailsViewModel(tradeForDetails: trade, trades: tradesSource));
481 + TradeDetailsViewModel(tradeForDetails: trade, trades: _tradesSource));
482
483 getIt.registerFactory(() => BackupService(
484 getIt.get<FlutterSecureStorage>(),
462 - getIt.get<AuthService>(),
463 - walletInfoSource,
485 + _walletInfoSource,
486 getIt.get<KeyService>(),
487 getIt.get<SharedPreferences>()));
488
@@ -476,8 +498,7 @@ Future setup(
498 getIt.registerFactory(
499 () => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>()));
500
479 - getIt.registerFactoryParam<RestoreOptionsPage, WalletType, void>(
480 - (WalletType type, _) => RestoreOptionsPage(type: type));
501 + getIt.registerFactory(() => RestoreOptionsPage());
502
503 getIt.registerFactory(
504 () => RestoreFromBackupViewModel(getIt.get<BackupService>()));
@@ -487,4 +508,6 @@ Future setup(
508
509 getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) =>
510 TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade)));
511 +
512 + _isSetupFinished = true;
513 }
lib/entities/contact.dart
+2 -1
@@ -5,11 +5,12 @@ import 'package:cake_wallet/utils/mobx.dart';
5
6 part 'contact.g.dart';
7
8 -@HiveType(typeId: 0)
8 +@HiveType(typeId: Contact.typeId)
9 class Contact extends HiveObject with Keyable {
10 Contact({@required this.name, @required this.address, CryptoCurrency type})
11 : raw = type?.raw;
12
13 + static const typeId = 0;
14 static const boxName = 'Contacts';
15
16 @HiveField(0)
lib/entities/default_settings_migration.dart
+25 -4
@@ -1,9 +1,12 @@
1 import 'dart:io' show File, Platform;
2 +import 'package:cake_wallet/core/generate_wallet_password.dart';
3 import 'package:cake_wallet/core/key_service.dart';
4 import 'package:cake_wallet/di.dart';
5 import 'package:cake_wallet/entities/pathForWallet.dart';
6 +import 'package:cake_wallet/entities/secret_store_key.dart';
7 import 'package:cake_wallet/monero/monero_wallet_service.dart';
8 import 'package:flutter/foundation.dart';
9 +import 'package:flutter_secure_storage/flutter_secure_storage.dart';
10 import 'package:hive/hive.dart';
11 import 'package:shared_preferences/shared_preferences.dart';
12 import 'package:cake_wallet/entities/preferences_key.dart';
@@ -17,10 +20,12 @@ import 'package:cake_wallet/entities/contact.dart';
20 import 'package:cake_wallet/entities/fs_migration.dart';
21 import 'package:cake_wallet/entities/wallet_info.dart';
22 import 'package:cake_wallet/exchange/trade.dart';
23 +import 'package:encrypt/encrypt.dart' as encrypt;
24
25 Future defaultSettingsMigration(
26 {@required int version,
27 @required SharedPreferences sharedPreferences,
28 + @required FlutterSecureStorage secureStorage,
29 @required Box<Node> nodes,
30 @required Box<WalletInfo> walletInfoSource,
31 @required Box<Trade> tradeSource,
@@ -29,9 +34,9 @@ Future defaultSettingsMigration(
34 await ios_migrate_v1(walletInfoSource, tradeSource, contactSource);
35 }
36
32 - final currentVersion =
33 - sharedPreferences.getInt('current_default_settings_migration_version') ??
34 - 0;
37 + final currentVersion = sharedPreferences
38 + .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) ??
39 + 0;
40 if (currentVersion >= version) {
41 return;
42 }
@@ -85,6 +90,10 @@ Future defaultSettingsMigration(
90 await updateDisplayModes(sharedPreferences);
91 break;
92
93 + case 9:
94 + await generateBackupPassword(secureStorage);
95 + break;
96 +
97 default:
98 break;
99 }
@@ -230,5 +239,17 @@ Future<void> updateDisplayModes(SharedPreferences sharedPreferences) async {
239 final currentBalanceDisplayMode =
240 sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey);
241 final balanceDisplayMode = currentBalanceDisplayMode < 2 ? 3 : 2;
233 - await sharedPreferences.setInt(PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode);
242 + await sharedPreferences.setInt(
243 + PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode);
244 +}
245 +
246 +Future<void> generateBackupPassword(FlutterSecureStorage secureStorage) async {
247 + final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
248 +
249 + if ((await secureStorage.read(key: key))?.isNotEmpty ?? false) {
250 + return;
251 + }
252 +
253 + final password = encrypt.Key.fromSecureRandom(32).base16;
254 + await secureStorage.write(key: key, value: password);
255 }
lib/entities/node.dart
+2 -1
@@ -8,7 +8,7 @@ import 'package:cake_wallet/entities/digest_request.dart';
8
9 part 'node.g.dart';
10
11 -@HiveType(typeId: 1)
11 +@HiveType(typeId: Node.typeId)
12 class Node extends HiveObject with Keyable {
13 Node(
14 {@required this.uri,
@@ -26,6 +26,7 @@ class Node extends HiveObject with Keyable {
26 typeRaw = map['typeRaw'] as int,
27 useSSL = map['useSSL'] as bool;
28
29 + static const typeId = 1;
30 static const boxName = 'Nodes';
31
32 @HiveField(0)
lib/entities/preferences_key.dart
+1
@@ -14,4 +14,5 @@ class PreferencesKey {
14 static const displayActionListModeKey = 'display_list_mode';
15 static const currentPinLength = 'current_pin_length';
16 static const currentLanguageCode = 'language_code';
17 + static const currentDefaultSettingsMigrationVersion = 'current_default_settings_migration_version';
18 }
\ No newline at end of file
lib/entities/template.dart
+2 -1
@@ -2,10 +2,11 @@ import 'package:hive/hive.dart';
2
3 part 'template.g.dart';
4
5 -@HiveType(typeId: 6)
5 +@HiveType(typeId: Template.typeId)
6 class Template extends HiveObject {
7 Template({this.name, this.address, this.cryptoCurrency, this.amount});
8
9 + static const typeId = 6;
10 static const boxName = 'Template';
11
12 @HiveField(0)
lib/entities/transaction_description.dart
+2 -1
@@ -2,10 +2,11 @@ import 'package:hive/hive.dart';
2
3 part 'transaction_description.g.dart';
4
5 -@HiveType(typeId: 2)
5 +@HiveType(typeId: TransactionDescription.typeId)
6 class TransactionDescription extends HiveObject {
7 TransactionDescription({this.id, this.recipientAddress, this.transactionNote});
8
9 + static const typeId = 2;
10 static const boxName = 'TransactionDescriptions';
11 static const boxKey = 'transactionDescriptionsBoxKey';
12
lib/entities/wallet_info.dart
+2 -1
@@ -4,7 +4,7 @@ import 'package:cake_wallet/entities/wallet_type.dart';
4
5 part 'wallet_info.g.dart';
6
7 -@HiveType(typeId: 4)
7 +@HiveType(typeId: WalletInfo.typeId)
8 class WalletInfo extends HiveObject {
9 WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight,
10 this.timestamp, this.dirPath, this.path, this.address);
@@ -23,6 +23,7 @@ class WalletInfo extends HiveObject {
23 date.millisecondsSinceEpoch ?? 0, dirPath, path, address);
24 }
25
26 + static const typeId = 4;
27 static const boxName = 'WalletInfo';
28
29 @HiveField(0)
lib/entities/wallet_type.dart
+2 -1
@@ -4,8 +4,9 @@ import 'package:hive/hive.dart';
4 part 'wallet_type.g.dart';
5
6 const walletTypes = [WalletType.monero, WalletType.bitcoin];
7 +const walletTypeTypeId = 5;
8
8 -@HiveType(typeId: 5)
9 +@HiveType(typeId: walletTypeTypeId)
10 enum WalletType {
11 @HiveField(0)
12 monero,
lib/exchange/exchange_template.dart
+2 -1
@@ -2,7 +2,7 @@ import 'package:hive/hive.dart';
2
3 part 'exchange_template.g.dart';
4
5 -@HiveType(typeId: 7)
5 +@HiveType(typeId: ExchangeTemplate.typeId)
6 class ExchangeTemplate extends HiveObject {
7 ExchangeTemplate({
8 this.amount,
@@ -13,6 +13,7 @@ class ExchangeTemplate extends HiveObject {
13 this.receiveAddress
14 });
15
16 + static const typeId = 7;
17 static const boxName = 'ExchangeTemplate';
18
19 @HiveField(0)
lib/exchange/trade.dart
+2 -1
@@ -6,7 +6,7 @@ import 'package:cake_wallet/entities/format_amount.dart';
6
7 part 'trade.g.dart';
8
9 -@HiveType(typeId: 3)
9 +@HiveType(typeId: Trade.typeId)
10 class Trade extends HiveObject {
11 Trade(
12 {this.id,
@@ -27,6 +27,7 @@ class Trade extends HiveObject {
27 toRaw = to?.raw,
28 stateRaw = state?.raw;
29
30 + static const typeId = 3;
31 static const boxName = 'Trades';
32 static const boxKey = 'tradesBoxKey';
33
lib/main.dart
+62 -37
@@ -1,7 +1,3 @@
1 -import 'package:cake_wallet/core/backup.dart';
2 -import 'package:cake_wallet/src/screens/backup/backup_page.dart';
3 -import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
4 -import 'package:cake_wallet/themes/theme_base.dart';
1 import 'package:flutter/material.dart';
2 import 'package:flutter/services.dart';
3 import 'package:hive/hive.dart';
@@ -12,6 +8,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
8 import 'package:flutter_secure_storage/flutter_secure_storage.dart';
9 import 'package:flutter_mobx/flutter_mobx.dart';
10 import 'package:cw_monero/wallet.dart' as monero_wallet;
11 +import 'package:cake_wallet/themes/theme_base.dart';
12 import 'package:cake_wallet/router.dart' as Router;
13 import 'package:cake_wallet/routes.dart';
14 import 'package:cake_wallet/generated/i18n.dart';
@@ -32,20 +29,46 @@ import 'package:cake_wallet/src/screens/root/root.dart';
29
30 final navigatorKey = GlobalKey<NavigatorState>();
31
35 -void main() async {
32 +Future<void> main() async {
33 try {
34 WidgetsFlutterBinding.ensureInitialized();
35
36 final appDir = await getApplicationDocumentsDirectory();
37 + await Hive.close();
38 Hive.init(appDir.path);
41 - Hive.registerAdapter(ContactAdapter());
42 - Hive.registerAdapter(NodeAdapter());
43 - Hive.registerAdapter(TransactionDescriptionAdapter());
44 - Hive.registerAdapter(TradeAdapter());
45 - Hive.registerAdapter(WalletInfoAdapter());
46 - Hive.registerAdapter(WalletTypeAdapter());
47 - Hive.registerAdapter(TemplateAdapter());
48 - Hive.registerAdapter(ExchangeTemplateAdapter());
39 +
40 + if (!Hive.isAdapterRegistered(Contact.typeId)) {
41 + Hive.registerAdapter(ContactAdapter());
42 + }
43 +
44 + if (!Hive.isAdapterRegistered(Node.typeId)) {
45 + Hive.registerAdapter(NodeAdapter());
46 + }
47 +
48 + if (!Hive.isAdapterRegistered(TransactionDescription.typeId)) {
49 + Hive.registerAdapter(TransactionDescriptionAdapter());
50 + }
51 +
52 + if (!Hive.isAdapterRegistered(Trade.typeId)) {
53 + Hive.registerAdapter(TradeAdapter());
54 + }
55 +
56 + if (!Hive.isAdapterRegistered(WalletInfo.typeId)) {
57 + Hive.registerAdapter(WalletInfoAdapter());
58 + }
59 +
60 + if (!Hive.isAdapterRegistered(walletTypeTypeId)) {
61 + Hive.registerAdapter(WalletTypeAdapter());
62 + }
63 +
64 + if (!Hive.isAdapterRegistered(Template.typeId)) {
65 + Hive.registerAdapter(TemplateAdapter());
66 + }
67 +
68 + if (!Hive.isAdapterRegistered(ExchangeTemplate.typeId)) {
69 + Hive.registerAdapter(ExchangeTemplateAdapter());
70 + }
71 +
72 final secureStorage = FlutterSecureStorage();
73 final transactionDescriptionsBoxKey = await getEncryptionKey(
74 secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
@@ -57,11 +80,11 @@ void main() async {
80 TransactionDescription.boxName,
81 encryptionKey: transactionDescriptionsBoxKey);
82 final trades =
60 - await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
83 + await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
84 final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
85 final templates = await Hive.openBox<Template>(Template.boxName);
86 final exchangeTemplates =
64 - await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
87 + await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
88 await initialSetup(
89 sharedPreferences: await SharedPreferences.getInstance(),
90 nodes: nodes,
@@ -72,7 +95,8 @@ void main() async {
95 templates: templates,
96 exchangeTemplates: exchangeTemplates,
97 transactionDescriptions: transactionDescriptions,
75 - initialMigrationVersion: 5);
98 + secureStorage: secureStorage,
99 + initialMigrationVersion: 9);
100 runApp(App());
101 } catch (e) {
102 runApp(MaterialApp(
@@ -80,7 +104,7 @@ void main() async {
104 home: Scaffold(
105 body: Container(
106 margin:
83 - EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
107 + EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
108 child: Text(
109 'Error:\n${e.toString()}',
110 style: TextStyle(fontSize: 22),
@@ -88,17 +112,20 @@ void main() async {
112 }
113 }
114
91 -Future<void> initialSetup({@required SharedPreferences sharedPreferences,
92 - @required Box<Node> nodes,
93 - @required Box<WalletInfo> walletInfoSource,
94 - @required Box<Contact> contactSource,
95 - @required Box<Trade> tradesSource,
96 - // @required FiatConvertationService fiatConvertationService,
97 - @required Box<Template> templates,
98 - @required Box<ExchangeTemplate> exchangeTemplates,
99 - @required Box<TransactionDescription> transactionDescriptions,
100 - int initialMigrationVersion = 6}) async {
115 +Future<void> initialSetup(
116 + {@required SharedPreferences sharedPreferences,
117 + @required Box<Node> nodes,
118 + @required Box<WalletInfo> walletInfoSource,
119 + @required Box<Contact> contactSource,
120 + @required Box<Trade> tradesSource,
121 + // @required FiatConvertationService fiatConvertationService,
122 + @required Box<Template> templates,
123 + @required Box<ExchangeTemplate> exchangeTemplates,
124 + @required Box<TransactionDescription> transactionDescriptions,
125 + FlutterSecureStorage secureStorage,
126 + int initialMigrationVersion = 9}) async {
127 await defaultSettingsMigration(
128 + secureStorage: secureStorage,
129 version: initialMigrationVersion,
130 sharedPreferences: sharedPreferences,
131 walletInfoSource: walletInfoSource,
@@ -113,7 +140,7 @@ Future<void> initialSetup({@required SharedPreferences sharedPreferences,
140 templates: templates,
141 exchangeTemplates: exchangeTemplates,
142 transactionDescriptionBox: transactionDescriptions);
116 - bootstrap(navigatorKey);
143 + await bootstrap(navigatorKey);
144 monero_wallet.onStartup();
145 }
146
@@ -125,16 +152,14 @@ class App extends StatelessWidget {
152
153 @override
154 Widget build(BuildContext context) {
128 - final settingsStore = getIt
129 - .get<AppStore>()
130 - .settingsStore;
131 - final statusBarColor = Colors.transparent;
132 - final authenticationStore = getIt.get<AuthenticationStore>();
133 - final initialRoute = authenticationStore.state == AuthenticationState.denied
134 - ? Routes.disclaimer
135 - : Routes.login;
136 -
155 return Observer(builder: (BuildContext context) {
156 + final settingsStore = getIt.get<AppStore>().settingsStore;
157 + final statusBarColor = Colors.transparent;
158 + final authenticationStore = getIt.get<AuthenticationStore>();
159 + final initialRoute =
160 + authenticationStore.state == AuthenticationState.denied
161 + ? Routes.disclaimer
162 + : Routes.login;
163 final currentTheme = settingsStore.currentTheme;
164 final statusBarBrightness = currentTheme.type == ThemeType.dark
165 ? Brightness.light
lib/reactions/bootstrap.dart
+6 -8
@@ -19,14 +19,12 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
19 final settingsStore = getIt.get<SettingsStore>();
20 final fiatConversionStore = getIt.get<FiatConversionStore>();
21
22 - if (authenticationStore.state == AuthenticationState.uninitialized) {
23 - final currentWalletName = getIt
24 - .get<SharedPreferences>()
25 - .getString(PreferencesKey.currentWalletName);
26 - authenticationStore.state = currentWalletName == null
27 - ? AuthenticationState.denied
28 - : AuthenticationState.installed;
29 - }
22 + final currentWalletName = getIt
23 + .get<SharedPreferences>()
24 + .getString(PreferencesKey.currentWalletName);
25 + authenticationStore.state = currentWalletName == null
26 + ? AuthenticationState.denied
27 + : AuthenticationState.installed;
28
29 startAuthenticationStateChange(authenticationStore, navigatorKey);
30 startCurrentWalletChangeReaction(
lib/router.dart
+1 -2
@@ -106,9 +106,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
106 param2: false));
107
108 case Routes.restoreOptions:
109 - final type = settings.arguments as WalletType;
109 return CupertinoPageRoute<void>(
111 - builder: (_) => getIt.get<RestoreOptionsPage>(param1: type));
110 + builder: (_) => getIt.get<RestoreOptionsPage>());
111
112 case Routes.restoreWalletOptions:
113 final type = WalletType.monero; //settings.arguments as WalletType;
lib/src/screens/backup/backup_page.dart
+18 -4
@@ -1,7 +1,9 @@
1 import 'package:flutter/material.dart';
2 import 'package:flutter/cupertino.dart';
3 +import 'package:flutter/services.dart';
4 import 'package:flutter_mobx/flutter_mobx.dart';
5 import 'package:esys_flutter_share/esys_flutter_share.dart';
6 +import 'package:cake_wallet/utils/show_bar.dart';
7 import 'package:cake_wallet/routes.dart';
8 import 'package:cake_wallet/generated/i18n.dart';
9 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
@@ -34,7 +36,7 @@ class BackupPage extends BasePage {
36 Center(
37 child: Container(
38 padding: EdgeInsets.only(left: 20, right: 20),
37 - height: 225,
39 + height: 300,
40 child: Column(children: [
41 Text(
42 'Backup password:',
@@ -43,9 +45,21 @@ class BackupPage extends BasePage {
45 Padding(
46 padding: EdgeInsets.only(top: 20, bottom: 10),
47 child: Observer(
46 - builder: (_) => Text(
47 - backupViewModelBase.backupPassword,
48 - style: TextStyle(fontSize: 26),
48 + builder: (_) => GestureDetector(
49 + onTap: () {
50 + Clipboard.setData(ClipboardData(
51 + text:
52 + backupViewModelBase.backupPassword));
53 + showBar<void>(
54 + context,
55 + S.of(context).transaction_details_copied(
56 + 'Backup password'));
57 + },
58 + child: Text(
59 + backupViewModelBase.backupPassword,
60 + style: TextStyle(fontSize: 26),
61 + textAlign: TextAlign.center,
62 + ),
63 ))),
64 Padding(
65 padding: EdgeInsets.all(20),
lib/src/screens/restore/restore_from_backup_page.dart
+1
@@ -84,5 +84,6 @@ class RestoreFromBackupPage extends BasePage {
84 }
85
86 await restoreFromBackupViewModel.import(textEditingController.text);
87 + textEditingController.text = '';
88 }
89 }
lib/src/screens/restore/restore_options_page.dart
+27 -32
@@ -1,4 +1,3 @@
1 -import 'package:cake_wallet/entities/wallet_type.dart';
1 import 'package:flutter/material.dart';
2 import 'package:cake_wallet/routes.dart';
3 import 'package:flutter/cupertino.dart';
@@ -7,10 +6,9 @@ import 'package:cake_wallet/src/screens/base_page.dart';
6 import 'package:cake_wallet/generated/i18n.dart';
7
8 class RestoreOptionsPage extends BasePage {
10 - RestoreOptionsPage({@required this.type});
11 -
9 + RestoreOptionsPage();
10 +
11 static const _aspectRatioImage = 2.086;
13 - final WalletType type;
12
13 @override
14 String get title => S.current.restore_restore_wallet;
@@ -21,33 +19,30 @@ class RestoreOptionsPage extends BasePage {
19 @override
20 Widget body(BuildContext context) {
21 return Container(
24 - width: double.infinity,
25 - height: double.infinity,
26 - padding: EdgeInsets.all(24),
27 - child: SingleChildScrollView(
28 - child: Column(
29 - children: <Widget>[
30 - RestoreButton(
31 - onPressed: () =>
32 - Navigator.pushNamed(
33 - context, Routes.restoreWalletOptionsFromWelcome),
34 - image: imageSeedKeys,
35 - title: S.of(context).restore_title_from_seed_keys,
36 - description: S.of(context).restore_description_from_seed_keys
37 - ),
38 - Padding(
39 - padding: EdgeInsets.only(top: 24),
40 - child: RestoreButton(
41 - onPressed: () => Navigator.pushNamed(
42 - context, Routes.restoreFromBackup),
43 - image: imageBackup,
44 - title: S.of(context).restore_title_from_backup,
45 - description: S.of(context).restore_description_from_backup
46 - ),
47 - )
48 - ],
49 - ),
50 - )
51 - );
22 + width: double.infinity,
23 + height: double.infinity,
24 + padding: EdgeInsets.all(24),
25 + child: SingleChildScrollView(
26 + child: Column(
27 + children: <Widget>[
28 + RestoreButton(
29 + onPressed: () =>
30 + Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome),
31 + image: imageSeedKeys,
32 + title: S.of(context).restore_title_from_seed_keys,
33 + description:
34 + S.of(context).restore_description_from_seed_keys),
35 + Padding(
36 + padding: EdgeInsets.only(top: 24),
37 + child: RestoreButton(
38 + onPressed: () =>
39 + Navigator.pushNamed(context, Routes.restoreFromBackup),
40 + image: imageBackup,
41 + title: S.of(context).restore_title_from_backup,
42 + description: S.of(context).restore_description_from_backup),
43 + )
44 + ],
45 + ),
46 + ));
47 }
48 }
lib/src/screens/welcome/welcome_page.dart
+1 -1
@@ -160,7 +160,7 @@ class WelcomePage extends BasePage {
160 child: PrimaryImageButton(
161 onPressed: () =>
162 Navigator.pushNamed(context,
163 - Routes.restoreWalletOptionsFromWelcome),
163 + Routes.restoreOptions),
164 image: restoreWalletImage,
165 text: S
166 .of(context)
lib/src/widgets/trail_button.dart
+1 -1
@@ -18,7 +18,7 @@ class TrailButton extends StatelessWidget {
18 caption,
19 style: TextStyle(
20 color:
21 - Theme.of(context).accentTextTheme.display4.decorationColor,
21 + Theme.of(context).accentTextTheme.bodyText2.color,
22 fontWeight: FontWeight.w500,
23 fontSize: 14),
24 ),
lib/store/settings_store.dart
+28 -5
@@ -85,10 +85,10 @@ abstract class SettingsStoreBase with Store {
85 (String languageCode) => sharedPreferences.setString(
86 PreferencesKey.currentLanguageCode, languageCode));
87
88 - reaction((_) => balanceDisplayMode,
89 - (BalanceDisplayMode mode) => sharedPreferences.setInt(
90 - PreferencesKey.currentBalanceDisplayModeKey,
91 - mode.serialize()));
88 + reaction(
89 + (_) => balanceDisplayMode,
90 + (BalanceDisplayMode mode) => sharedPreferences.setInt(
91 + PreferencesKey.currentBalanceDisplayModeKey, mode.serialize()));
92
93 this
94 .nodes
@@ -158,7 +158,7 @@ abstract class SettingsStoreBase with Store {
158 .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
159 false;
160 final legacyTheme =
161 - (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false)
161 + (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false)
162 ? ThemeType.dark.index
163 : ThemeType.bright.index;
164 final savedTheme = ThemeList.deserialize(
@@ -203,6 +203,29 @@ abstract class SettingsStoreBase with Store {
203 initialLanguageCode: savedLanguageCode);
204 }
205
206 + Future<void> reload(
207 + {@required Box<Node> nodeSource,
208 + FiatCurrency initialFiatCurrency = FiatCurrency.usd,
209 + TransactionPriority initialTransactionPriority = TransactionPriority.slow,
210 + BalanceDisplayMode initialBalanceDisplayMode =
211 + BalanceDisplayMode.availableBalance}) async {
212 + final settings = await SettingsStoreBase.load(
213 + nodeSource: nodeSource,
214 + initialBalanceDisplayMode: initialBalanceDisplayMode,
215 + initialFiatCurrency: initialFiatCurrency,
216 + initialTransactionPriority: initialTransactionPriority);
217 + fiatCurrency = settings.fiatCurrency;
218 + actionlistDisplayMode = settings.actionlistDisplayMode;
219 + transactionPriority = settings.transactionPriority;
220 + balanceDisplayMode = settings.balanceDisplayMode;
221 + shouldSaveRecipientAddress = settings.shouldSaveRecipientAddress;
222 + allowBiometricalAuthentication = settings.allowBiometricalAuthentication;
223 + currentTheme = settings.currentTheme;
224 + pinCodeLength = settings.pinCodeLength;
225 + languageCode = settings.languageCode;
226 + appVersion = settings.appVersion;
227 + }
228 +
229 Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
230 switch (walletType) {
231 case WalletType.bitcoin:
lib/view_model/backup_view_model.dart
+3 -2
@@ -1,4 +1,4 @@
1 -import 'package:cake_wallet/core/backup.dart';
1 +import 'package:cake_wallet/core/backup_service.dart';
2 import 'package:cake_wallet/core/execution_state.dart';
3 import 'package:cake_wallet/entities/secret_store_key.dart';
4 import 'package:cake_wallet/store/secret_store.dart';
@@ -52,7 +52,7 @@ abstract class BackupViewModelBase with Store {
52 Future<BackupExportFile> exportBackup() async {
53 try {
54 state = IsExecutingState();
55 - final backupContent = await backupService.exportBackup('', nonce: '');
55 + final backupContent = await backupService.exportBackup(backupPassword);
56 state = ExecutedSuccessfullyState();
57
58 return BackupExportFile(backupContent.toList(),
@@ -60,6 +60,7 @@ abstract class BackupViewModelBase with Store {
60 } catch (e) {
61 print(e.toString());
62 state = FailureState(e.toString());
63 + return null;
64 }
65 }
66
lib/view_model/restore_from_backup_view_model.dart
+39 -12
@@ -1,11 +1,17 @@
1 import 'dart:io';
2 -
3 -import 'package:cake_wallet/core/backup.dart';
2 +import 'package:hive/hive.dart';
3 import 'package:mobx/mobx.dart';
4 +import 'package:cake_wallet/main.dart';
5 +import 'package:cake_wallet/di.dart';
6 +import 'package:cake_wallet/core/backup_service.dart';
7 +import 'package:cake_wallet/entities/node.dart';
8 +import 'package:cake_wallet/store/app_store.dart';
9 +import 'package:cake_wallet/store/authentication_store.dart';
10
11 part 'restore_from_backup_view_model.g.dart';
12
8 -class RestoreFromBackupViewModel = RestoreFromBackupViewModelBase with _$RestoreFromBackupViewModel;
13 +class RestoreFromBackupViewModel = RestoreFromBackupViewModelBase
14 + with _$RestoreFromBackupViewModel;
15
16 abstract class RestoreFromBackupViewModelBase with Store {
17 RestoreFromBackupViewModelBase(this.backupService);
@@ -15,15 +21,36 @@ abstract class RestoreFromBackupViewModelBase with Store {
21
22 final BackupService backupService;
23
24 + @action
25 + void reset() => filePath = '';
26 +
27 Future<void> import(String password) async {
19 - if (filePath?.isEmpty ?? true) {
20 - // FIXME: throw exception;
21 - return;
28 + try {
29 + if (filePath?.isEmpty ?? true) {
30 + // FIXME: throw exception;
31 + return;
32 + }
33 +
34 + final file = File(filePath);
35 + final data = await file.readAsBytes();
36 +
37 + await backupService.importBackup(data, password);
38 + await main();
39 +
40 + final store = getIt.get<AppStore>();
41 + ReactionDisposer reaction;
42 + await store.settingsStore.reload(nodeSource: getIt.get<Box<Node>>());
43 +
44 + reaction = autorun((_) {
45 + final wallet = store.wallet;
46 +
47 + if (wallet != null) {
48 + store.authenticationStore.state = AuthenticationState.allowed;
49 + reaction?.reaction?.dispose();
50 + }
51 + });
52 + } catch (e) {
53 + print(e.toString());
54 }
23 -
24 - final file = File(filePath);
25 - final data = await file.readAsBytes();
26 -
27 - await backupService.importBackup(data, password, nonce: null);
55 }
29 -}
\ No newline at end of file
56 +}
lib/view_model/settings/settings_view_model.dart
+13
@@ -122,6 +122,19 @@ abstract class SettingsViewModelBase with Store {
122 onItemSelected: (ThemeBase theme) =>
123 _settingsStore.currentTheme = theme)
124 ],
125 + [
126 + RegularListItem(
127 + title: 'Backup',
128 + handler: (BuildContext context) {
129 + Navigator.of(context).pushNamed(Routes.auth, arguments:
130 + (bool isAuthenticatedSuccessfully, AuthPageState auth) {
131 + auth.close();
132 + if (isAuthenticatedSuccessfully) {
133 + Navigator.of(context).pushNamed(Routes.backup);
134 + }
135 + });
136 + }),
137 + ],
138 [
139 LinkListItem(
140 title: 'Email',