Cw 528 backup wallet files (#1281)

* monero wallet backup changes * [skipci] updates * monero fixes * start work for bitcoin/eth * cleanup * [skipci] more cleanup * add all other coins * merge fixes * add corrupted test * build for testing * actually be able to test monero * review fixes * more review fixes

Matthew Fosse committed Feb 7, 2024 at 07:44 UTC 26fe28891debf378dbacfb6417f40dbfdccf2716
12 files changed +266 -119
cw_bitcoin/lib/bitcoin_wallet_service.dart
+32 -22
@@ -12,10 +12,8 @@ import 'package:cw_core/wallet_type.dart';
12 import 'package:hive/hive.dart';
13 import 'package:collection/collection.dart';
14
15 -class BitcoinWalletService extends WalletService<
16 - BitcoinNewWalletCredentials,
17 - BitcoinRestoreWalletFromSeedCredentials,
18 - BitcoinRestoreWalletFromWIFCredentials> {
15 +class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
16 + BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
17 BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
18
19 final Box<WalletInfo> walletInfoSource;
@@ -42,28 +40,41 @@ class BitcoinWalletService extends WalletService<
40
41 @override
42 Future<BitcoinWallet> openWallet(String name, String password) async {
45 - final walletInfo = walletInfoSource.values.firstWhereOrNull(
46 - (info) => info.id == WalletBase.idFor(name, getType()))!;
47 - final wallet = await BitcoinWalletBase.open(
48 - password: password, name: name, walletInfo: walletInfo,
49 - unspentCoinsInfo: unspentCoinsInfoSource);
50 - await wallet.init();
51 - return wallet;
43 + final walletInfo = walletInfoSource.values
44 + .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
45 + try {
46 + final wallet = await BitcoinWalletBase.open(
47 + password: password,
48 + name: name,
49 + walletInfo: walletInfo,
50 + unspentCoinsInfo: unspentCoinsInfoSource);
51 + await wallet.init();
52 + saveBackup(name);
53 + return wallet;
54 + } catch (_) {
55 + await restoreWalletFilesFromBackup(name);
56 + final wallet = await BitcoinWalletBase.open(
57 + password: password,
58 + name: name,
59 + walletInfo: walletInfo,
60 + unspentCoinsInfo: unspentCoinsInfoSource);
61 + await wallet.init();
62 + return wallet;
63 + }
64 }
65
66 @override
67 Future<void> remove(String wallet) async {
56 - File(await pathForWalletDir(name: wallet, type: getType()))
57 - .delete(recursive: true);
58 - final walletInfo = walletInfoSource.values.firstWhereOrNull(
59 - (info) => info.id == WalletBase.idFor(wallet, getType()))!;
68 + File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
69 + final walletInfo = walletInfoSource.values
70 + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
71 await walletInfoSource.delete(walletInfo.key);
72 }
73
74 @override
75 Future<void> rename(String currentName, String password, String newName) async {
65 - final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
66 - (info) => info.id == WalletBase.idFor(currentName, getType()))!;
76 + final currentWalletInfo = walletInfoSource.values
77 + .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
78 final currentWallet = await BitcoinWalletBase.open(
79 password: password,
80 name: currentName,
@@ -71,6 +82,7 @@ class BitcoinWalletService extends WalletService<
82 unspentCoinsInfo: unspentCoinsInfoSource);
83
84 await currentWallet.renameWalletFiles(newName);
85 + await saveBackup(newName);
86
87 final newWalletInfo = currentWalletInfo;
88 newWalletInfo.id = WalletBase.idFor(newName, getType());
@@ -80,13 +92,11 @@ class BitcoinWalletService extends WalletService<
92 }
93
94 @override
83 - Future<BitcoinWallet> restoreFromKeys(
84 - BitcoinRestoreWalletFromWIFCredentials credentials) async =>
95 + Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
96 throw UnimplementedError();
97
98 @override
88 - Future<BitcoinWallet> restoreFromSeed(
89 - BitcoinRestoreWalletFromSeedCredentials credentials) async {
99 + Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
100 if (!validateMnemonic(credentials.mnemonic)) {
101 throw BitcoinMnemonicIsIncorrectException();
102 }
@@ -100,4 +110,4 @@ class BitcoinWalletService extends WalletService<
110 await wallet.init();
111 return wallet;
112 }
103 -}
\ No newline at end of file
113 +}
cw_bitcoin/lib/electrum_wallet.dart
-3
@@ -601,8 +601,6 @@ abstract class ElectrumWalletBase
601 electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
602 final historyResults = await Future.wait(histories);
603
604 -
605 -
604 historyResults.forEach((history) {
605 history.entries.forEach((historyItem) {
606 if (historyItem.value.isNotEmpty) {
@@ -622,7 +620,6 @@ abstract class ElectrumWalletBase
620 }
621 }
622
625 -
623 addressHashes.forEach((sh, addressRecord) {
624 addressRecord.txCount = newTxCounts[sh] ?? 0;
625 });
cw_bitcoin/lib/litecoin_wallet_service.dart
+17 -5
@@ -45,11 +45,22 @@ class LitecoinWalletService extends WalletService<
45 Future<LitecoinWallet> openWallet(String name, String password) async {
46 final walletInfo = walletInfoSource.values.firstWhereOrNull(
47 (info) => info.id == WalletBase.idFor(name, getType()))!;
48 - final wallet = await LitecoinWalletBase.open(
49 - password: password, name: name, walletInfo: walletInfo,
50 - unspentCoinsInfo: unspentCoinsInfoSource);
51 - await wallet.init();
52 - return wallet;
48 +
49 + try {
50 + final wallet = await LitecoinWalletBase.open(
51 + password: password, name: name, walletInfo: walletInfo,
52 + unspentCoinsInfo: unspentCoinsInfoSource);
53 + await wallet.init();
54 + saveBackup(name);
55 + return wallet;
56 + } catch (_) {
57 + await restoreWalletFilesFromBackup(name);
58 + final wallet = await LitecoinWalletBase.open(
59 + password: password, name: name, walletInfo: walletInfo,
60 + unspentCoinsInfo: unspentCoinsInfoSource);
61 + await wallet.init();
62 + return wallet;
63 + }
64 }
65
66 @override
@@ -72,6 +83,7 @@ class LitecoinWalletService extends WalletService<
83 unspentCoinsInfo: unspentCoinsInfoSource);
84
85 await currentWallet.renameWalletFiles(newName);
86 + await saveBackup(newName);
87
88 final newWalletInfo = currentWalletInfo;
89 newWalletInfo.id = WalletBase.idFor(newName, getType());
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart
+17 -5
@@ -51,11 +51,22 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
51 Future<BitcoinCashWallet> openWallet(String name, String password) async {
52 final walletInfo = walletInfoSource.values.firstWhereOrNull(
53 (info) => info.id == WalletBase.idFor(name, getType()))!;
54 - final wallet = await BitcoinCashWalletBase.open(
55 - password: password, name: name, walletInfo: walletInfo,
56 - unspentCoinsInfo: unspentCoinsInfoSource);
57 - await wallet.init();
58 - return wallet;
54 +
55 + try {
56 + final wallet = await BitcoinCashWalletBase.open(
57 + password: password, name: name, walletInfo: walletInfo,
58 + unspentCoinsInfo: unspentCoinsInfoSource);
59 + await wallet.init();
60 + saveBackup(name);
61 + return wallet;
62 + } catch(_) {
63 + await restoreWalletFilesFromBackup(name);
64 + final wallet = await BitcoinCashWalletBase.open(
65 + password: password, name: name, walletInfo: walletInfo,
66 + unspentCoinsInfo: unspentCoinsInfoSource);
67 + await wallet.init();
68 + return wallet;
69 + }
70 }
71
72 @override
@@ -78,6 +89,7 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
89 unspentCoinsInfo: unspentCoinsInfoSource);
90
91 await currentWallet.renameWalletFiles(newName);
92 + await saveBackup(newName);
93
94 final newWalletInfo = currentWalletInfo;
95 newWalletInfo.id = WalletBase.idFor(newName, getType());
cw_core/lib/monero_wallet_utils.dart
+15 -4
@@ -54,6 +54,17 @@ Future<void> restoreWalletFiles(String name) async {
54 }
55 }
56
57 +Future<void> resetCache(String name) async {
58 + await removeCache(name);
59 +
60 + final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
61 + final cacheFilePath = '$walletDirPath/$name';
62 + final backupCacheFile = File(backupFileName(cacheFilePath));
63 + if (backupCacheFile.existsSync()) {
64 + await backupCacheFile.copy(cacheFilePath);
65 + }
66 +}
67 +
68 Future<bool> backupWalletFilesExists(String name) async {
69 final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
70 final cacheFilePath = '$walletDirPath/$name';
@@ -63,9 +74,9 @@ Future<bool> backupWalletFilesExists(String name) async {
74 final backupKeysFile = File(backupFileName(keysFilePath));
75 final backupAddressListFile = File(backupFileName(addressListFilePath));
76
66 - return backupCacheFile.existsSync()
67 - && backupKeysFile.existsSync()
68 - && backupAddressListFile.existsSync();
77 + return backupCacheFile.existsSync() &&
78 + backupKeysFile.existsSync() &&
79 + backupAddressListFile.existsSync();
80 }
81
82 Future<void> removeCache(String name) async {
@@ -85,4 +96,4 @@ Future<void> restoreOrResetWalletFiles(String name) async {
96 }
97
98 removeCache(name);
88 -}
\ No newline at end of file
99 +}
cw_core/lib/wallet_service.dart
+21 -2
@@ -1,7 +1,8 @@
1 -import 'package:cw_core/node.dart';
1 +import 'dart:io';
2 +
3 +import 'package:cw_core/pathForWallet.dart';
4 import 'package:cw_core/wallet_base.dart';
5 import 'package:cw_core/wallet_credentials.dart';
4 -import 'package:cw_core/wallet_info.dart';
6 import 'package:cw_core/wallet_type.dart';
7
8 abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
@@ -21,4 +22,22 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
22 Future<void> remove(String wallet);
23
24 Future<void> rename(String currentName, String password, String newName);
25 +
26 + Future<void> restoreWalletFilesFromBackup(String name) async {
27 + final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType());
28 + final walletDirPath = await pathForWalletDir(name: name, type: getType());
29 +
30 + if (File(backupWalletDirPath).existsSync()) {
31 + await File(backupWalletDirPath).copy(walletDirPath);
32 + }
33 + }
34 +
35 + Future<void> saveBackup(String name) async {
36 + final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType());
37 + final walletDirPath = await pathForWalletDir(name: name, type: getType());
38 +
39 + if (File(walletDirPath).existsSync()) {
40 + await File(walletDirPath).copy(backupWalletDirPath);
41 + }
42 + }
43 }
cw_ethereum/lib/ethereum_wallet_service.dart
+25 -9
@@ -39,16 +39,31 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
39 Future<EthereumWallet> openWallet(String name, String password) async {
40 final walletInfo =
41 walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
42 - final wallet = await EthereumWallet.open(
43 - name: name,
44 - password: password,
45 - walletInfo: walletInfo,
46 - );
47 -
48 - await wallet.init();
49 - await wallet.save();
42
51 - return wallet;
43 + try {
44 + final wallet = await EthereumWallet.open(
45 + name: name,
46 + password: password,
47 + walletInfo: walletInfo,
48 + );
49 +
50 + await wallet.init();
51 + await wallet.save();
52 + saveBackup(name);
53 + return wallet;
54 + } catch (_) {
55 +
56 + await restoreWalletFilesFromBackup(name);
57 +
58 + final wallet = await EthereumWallet.open(
59 + name: name,
60 + password: password,
61 + walletInfo: walletInfo,
62 + );
63 + await wallet.init();
64 + await wallet.save();
65 + return wallet;
66 + }
67 }
68
69 @override
@@ -59,6 +74,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
74 password: password, name: currentName, walletInfo: currentWalletInfo);
75
76 await currentWallet.renameWalletFiles(newName);
77 + await saveBackup(newName);
78
79 final newWalletInfo = currentWalletInfo;
80 newWalletInfo.id = WalletBase.idFor(newName, getType());
cw_haven/lib/haven_wallet_service.dart
+1
@@ -163,6 +163,7 @@ class HavenWalletService extends WalletService<
163 final currentWallet = HavenWallet(walletInfo: currentWalletInfo);
164
165 await currentWallet.renameWalletFiles(newName);
166 + await saveBackup(newName);
167
168 final newWalletInfo = currentWalletInfo;
169 newWalletInfo.id = WalletBase.idFor(newName, getType());
cw_monero/lib/monero_wallet.dart
+52 -12
@@ -36,6 +36,8 @@ import 'package:mobx/mobx.dart';
36 part 'monero_wallet.g.dart';
37
38 const moneroBlockSize = 1000;
39 +// not sure if this should just be 0 but setting it higher feels safer / should catch more cases:
40 +const MIN_RESTORE_HEIGHT = 1000;
41
42 class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
43
@@ -79,7 +81,7 @@ abstract class MoneroWalletBase
81
82 Box<UnspentCoinsInfo> unspentCoinsInfo;
83
82 - void Function(FlutterErrorDetails)? _onError;
84 + void Function(FlutterErrorDetails)? onError;
85
86 @override
87 late MoneroWalletAddresses walletAddresses;
@@ -171,7 +173,26 @@ abstract class MoneroWalletBase
173 Future<void> startSync() async {
174 try {
175 _setInitialHeight();
174 - } catch (_) {}
176 + } catch (_) {
177 + // our restore height wasn't correct, so lets see if using the backup works:
178 + try {
179 + await resetCache(name);
180 + _setInitialHeight();
181 + } catch (e) {
182 + // we still couldn't get a valid height from the backup?!:
183 + // try to use the date instead:
184 + try {
185 + _setHeightFromDate();
186 + } catch (e, s) {
187 + // we still couldn't get a valid sync height :/
188 + onError?.call(FlutterErrorDetails(
189 + exception: e,
190 + stack: s,
191 + library: this.runtimeType.toString(),
192 + ));
193 + }
194 + }
195 + }
196
197 try {
198 syncStatus = AttemptingSyncStatus();
@@ -339,6 +360,8 @@ abstract class MoneroWalletBase
360 if (currentAddressListFile.existsSync()) {
361 await currentAddressListFile.rename('$newWalletPath.address.txt');
362 }
363 +
364 + await backupWalletFiles(newWalletName);
365 } catch (e) {
366 final currentWalletPath = await pathForWallet(name: name, type: type);
367
@@ -402,9 +425,7 @@ abstract class MoneroWalletBase
425 if (coin.spent == 0) {
426 final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
427 if (unspent.hash.isNotEmpty) {
405 - unspent.isChange = transaction_history
406 - .getTransaction(unspent.hash)
407 - .direction == 1;
428 + unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1;
429 }
430 unspentCoins.add(unspent);
431 }
@@ -418,7 +439,7 @@ abstract class MoneroWalletBase
439 if (unspentCoins.isNotEmpty) {
440 unspentCoins.forEach((coin) {
441 final coinInfoList = unspentCoinsInfo.values.where((element) =>
421 - element.walletId.contains(id) &&
442 + element.walletId.contains(id) &&
443 element.accountIndex == walletAddresses.account!.id &&
444 element.keyImage!.contains(coin.keyImage!));
445
@@ -438,7 +459,7 @@ abstract class MoneroWalletBase
459 _askForUpdateBalance();
460 } catch (e, s) {
461 print(e.toString());
441 - _onError?.call(FlutterErrorDetails(
462 + onError?.call(FlutterErrorDetails(
463 exception: e,
464 stack: s,
465 library: this.runtimeType.toString(),
@@ -534,18 +555,36 @@ abstract class MoneroWalletBase
555 _listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction);
556 }
557
558 + // check if the height is correct:
559 void _setInitialHeight() {
560 if (walletInfo.isRecovery) {
561 return;
562 }
563
542 - final currentHeight = monero_wallet.getCurrentHeight();
564 + final height = monero_wallet.getCurrentHeight();
565 +
566 + if (height > MIN_RESTORE_HEIGHT) {
567 + // the restore height is probably correct, so we do nothing:
568 + return;
569 + }
570 +
571 + throw Exception("height isn't > $MIN_RESTORE_HEIGHT!");
572 + }
573 +
574 + void _setHeightFromDate() {
575 + if (walletInfo.isRecovery) {
576 + return;
577 + }
578 +
579 + final height = _getHeightByDate(walletInfo.date);
580
544 - if (currentHeight <= 1) {
545 - final height = _getHeightByDate(walletInfo.date);
581 + if (height > MIN_RESTORE_HEIGHT) {
582 monero_wallet.setRecoveringFromSeed(isRecovery: true);
583 monero_wallet.setRefreshFromBlockHeight(height: height);
584 + return;
585 }
586 +
587 + throw Exception("height isn't > $MIN_RESTORE_HEIGHT!");
588 }
589
590 int _getHeightDistance(DateTime date) {
@@ -561,7 +600,8 @@ abstract class MoneroWalletBase
600 final heightDistance = _getHeightDistance(date);
601
602 if (nodeHeight <= 0) {
564 - return 0;
603 + // the node returned 0 (an error state), so lets just restore from cache:
604 + throw Exception("nodeHeight is <= 0!");
605 }
606
607 return nodeHeight - heightDistance;
@@ -650,7 +690,7 @@ abstract class MoneroWalletBase
690 }
691
692 @override
653 - void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
693 + void setExceptionHandler(void Function(FlutterErrorDetails) e) => onError = e;
694
695 @override
696 String signMessage(String message, {String? address}) {
cw_monero/lib/monero_wallet_service.dart
+37 -40
@@ -11,11 +11,13 @@ import 'package:cw_core/get_height_by_date.dart';
11 import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
12 import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
13 import 'package:cw_monero/monero_wallet.dart';
14 +import 'package:flutter/widgets.dart';
15 import 'package:hive/hive.dart';
16 import 'package:polyseed/polyseed.dart';
17
18 class MoneroNewWalletCredentials extends WalletCredentials {
18 - MoneroNewWalletCredentials({required String name, required this.language, required this.isPolyseed, String? password})
19 + MoneroNewWalletCredentials(
20 + {required String name, required this.language, required this.isPolyseed, String? password})
21 : super(name: name, password: password);
22
23 final String language;
@@ -52,10 +54,8 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
54 final String spendKey;
55 }
56
55 -class MoneroWalletService extends WalletService<
56 - MoneroNewWalletCredentials,
57 - MoneroRestoreWalletFromSeedCredentials,
58 - MoneroRestoreWalletFromKeysCredentials> {
57 +class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
58 + MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials> {
59 MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
60
61 final Box<WalletInfo> walletInfoSource;
@@ -112,6 +112,7 @@ class MoneroWalletService extends WalletService<
112
113 @override
114 Future<MoneroWallet> openWallet(String name, String password) async {
115 + MoneroWallet? wallet;
116 try {
117 final path = await pathForWallet(name: name, type: getType());
118
@@ -119,11 +120,10 @@ class MoneroWalletService extends WalletService<
120 await repairOldAndroidWallet(name);
121 }
122
122 - await monero_wallet_manager
123 - .openWalletAsync({'path': path, 'password': password});
124 - final walletInfo = walletInfoSource.values.firstWhere(
125 - (info) => info.id == WalletBase.idFor(name, getType()));
126 - final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
123 + await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
124 + final walletInfo = walletInfoSource.values
125 + .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
126 + wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
127 final isValid = wallet.walletAddresses.validate();
128
129 if (!isValid) {
@@ -135,7 +135,7 @@ class MoneroWalletService extends WalletService<
135 await wallet.init();
136
137 return wallet;
138 - } catch (e) {
138 + } catch (e, s) {
139 // TODO: Implement Exception for wallet list service.
140
141 final bool isBadAlloc = e.toString().contains('bad_alloc') ||
@@ -156,16 +156,18 @@ class MoneroWalletService extends WalletService<
156 final bool invalidSignature = e.toString().contains('invalid signature') ||
157 (e is WalletOpeningException && e.message.contains('invalid signature'));
158
159 - if (isBadAlloc ||
160 - doesNotCorrespond ||
161 - isMissingCacheFilesIOS ||
162 - isMissingCacheFilesAndroid ||
163 - invalidSignature) {
164 - await restoreOrResetWalletFiles(name);
165 - return openWallet(name, password);
159 + if (!isBadAlloc &&
160 + !doesNotCorrespond &&
161 + !isMissingCacheFilesIOS &&
162 + !isMissingCacheFilesAndroid &&
163 + !invalidSignature &&
164 + wallet != null &&
165 + wallet.onError != null) {
166 + wallet.onError!(FlutterErrorDetails(exception: e, stack: s));
167 }
168
168 - rethrow;
169 + await restoreOrResetWalletFiles(name);
170 + return openWallet(name, password);
171 }
172 }
173
@@ -185,10 +187,9 @@ class MoneroWalletService extends WalletService<
187 }
188
189 @override
188 - Future<void> rename(
189 - String currentName, String password, String newName) async {
190 - final currentWalletInfo = walletInfoSource.values.firstWhere(
191 - (info) => info.id == WalletBase.idFor(currentName, getType()));
190 + Future<void> rename(String currentName, String password, String newName) async {
191 + final currentWalletInfo = walletInfoSource.values
192 + .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
193 final currentWallet =
194 MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
195
@@ -202,8 +203,7 @@ class MoneroWalletService extends WalletService<
203 }
204
205 @override
205 - Future<MoneroWallet> restoreFromKeys(
206 - MoneroRestoreWalletFromKeysCredentials credentials) async {
206 + Future<MoneroWallet> restoreFromKeys(MoneroRestoreWalletFromKeysCredentials credentials) async {
207 try {
208 final path = await pathForWallet(name: credentials.name, type: getType());
209 await monero_wallet_manager.restoreFromKeys(
@@ -227,9 +227,7 @@ class MoneroWalletService extends WalletService<
227 }
228
229 @override
230 - Future<MoneroWallet> restoreFromSeed(
231 - MoneroRestoreWalletFromSeedCredentials credentials) async {
232 -
230 + Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials) async {
231 // Restore from Polyseed
232 if (Polyseed.isValidSeed(credentials.mnemonic)) {
233 return restoreFromPolyseed(credentials);
@@ -254,14 +252,16 @@ class MoneroWalletService extends WalletService<
252 }
253 }
254
257 - Future<MoneroWallet> restoreFromPolyseed(MoneroRestoreWalletFromSeedCredentials credentials) async {
255 + Future<MoneroWallet> restoreFromPolyseed(
256 + MoneroRestoreWalletFromSeedCredentials credentials) async {
257 try {
258 final path = await pathForWallet(name: credentials.name, type: getType());
259 final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
260 final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
261 final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
262
264 - return _restoreFromPolyseed(path, credentials.password!, polyseed, credentials.walletInfo!, lang);
263 + return _restoreFromPolyseed(
264 + path, credentials.password!, polyseed, credentials.walletInfo!, lang);
265 } catch (e) {
266 // TODO: Implement Exception for wallet list service.
267 print('MoneroWalletsManager Error: $e');
@@ -269,11 +269,11 @@ class MoneroWalletService extends WalletService<
269 }
270 }
271
272 - Future<MoneroWallet> _restoreFromPolyseed(String path, String password, Polyseed polyseed,
273 - WalletInfo walletInfo, PolyseedLang lang,
272 + Future<MoneroWallet> _restoreFromPolyseed(
273 + String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
274 {PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
275 - final height = overrideHeight ?? getMoneroHeigthByDate(
276 - date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
275 + final height = overrideHeight ??
276 + getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
277 final spendKey = polyseed.generateKey(coin, 32).toHexString();
278 final seed = polyseed.encode(lang, coin);
279
@@ -288,8 +288,7 @@ class MoneroWalletService extends WalletService<
288 restoreHeight: height,
289 spendKey: spendKey);
290
291 - final wallet = MoneroWallet(
292 - walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
291 + final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
292 await wallet.init();
293
294 return wallet;
@@ -301,16 +300,14 @@ class MoneroWalletService extends WalletService<
300 return;
301 }
302
304 - final oldAndroidWalletDirPath =
305 - await outdatedAndroidPathForWalletDir(name: name);
303 + final oldAndroidWalletDirPath = await outdatedAndroidPathForWalletDir(name: name);
304 final dir = Directory(oldAndroidWalletDirPath);
305
306 if (!dir.existsSync()) {
307 return;
308 }
309
312 - final newWalletDirPath =
313 - await pathForWalletDir(name: name, type: getType());
310 + final newWalletDirPath = await pathForWalletDir(name: name, type: getType());
311
312 dir.listSync().forEach((f) {
313 final file = File(f.path);
cw_nano/lib/nano_wallet_service.dart
+24 -8
@@ -69,6 +69,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
69 NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
70
71 await currentWallet.renameWalletFiles(newName);
72 + await saveBackup(newName);
73
74 final newWalletInfo = currentWalletInfo;
75 newWalletInfo.id = WalletBase.idFor(newName, getType());
@@ -150,14 +151,29 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
151 Future<NanoWallet> openWallet(String name, String password) async {
152 final walletInfo =
153 walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
153 - final wallet = await NanoWalletBase.open(
154 - name: name,
155 - password: password,
156 - walletInfo: walletInfo,
157 - );
154
159 - await wallet.init();
160 - await wallet.save();
161 - return wallet;
155 + try {
156 + final wallet = await NanoWalletBase.open(
157 + name: name,
158 + password: password,
159 + walletInfo: walletInfo,
160 + );
161 +
162 + await wallet.init();
163 + await wallet.save();
164 + saveBackup(name);
165 + return wallet;
166 + } catch (_) {
167 + await restoreWalletFilesFromBackup(name);
168 + final wallet = await NanoWalletBase.open(
169 + name: name,
170 + password: password,
171 + walletInfo: walletInfo,
172 + );
173 +
174 + await wallet.init();
175 + await wallet.save();
176 + return wallet;
177 + }
178 }
179 }
cw_polygon/lib/polygon_wallet_service.dart
+25 -9
@@ -42,16 +42,31 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
42 Future<PolygonWallet> openWallet(String name, String password) async {
43 final walletInfo =
44 walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
45 - final wallet = await PolygonWallet.open(
46 - name: name,
47 - password: password,
48 - walletInfo: walletInfo,
49 - );
50 -
51 - await wallet.init();
52 - await wallet.save();
45
54 - return wallet;
46 + try {
47 + final wallet = await PolygonWallet.open(
48 + name: name,
49 + password: password,
50 + walletInfo: walletInfo,
51 + );
52 +
53 + await wallet.init();
54 + await wallet.save();
55 + saveBackup(name);
56 + return wallet;
57 + } catch (_) {
58 + await restoreWalletFilesFromBackup(name);
59 +
60 + final wallet = await PolygonWallet.open(
61 + name: name,
62 + password: password,
63 + walletInfo: walletInfo,
64 + );
65 +
66 + await wallet.init();
67 + await wallet.save();
68 + return wallet;
69 + }
70 }
71
72 @override
@@ -100,6 +115,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
115 password: password, name: currentName, walletInfo: currentWalletInfo);
116
117 await currentWallet.renameWalletFiles(newName);
118 + await saveBackup(newName);
119
120 final newWalletInfo = currentWalletInfo;
121 newWalletInfo.id = WalletBase.idFor(newName, getType());