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',