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
This commit is contained in:
Matthew Fosse 2024-02-07 07:44:29 -08:00 committed by GitHub
parent e4ddf82e69
commit 26fe28891d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 264 additions and 117 deletions

View file

@ -12,10 +12,8 @@ import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
class BitcoinWalletService extends WalletService< class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials> {
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
@ -42,28 +40,41 @@ class BitcoinWalletService extends WalletService<
@override @override
Future<BitcoinWallet> openWallet(String name, String password) async { Future<BitcoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(name, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
try {
final wallet = await BitcoinWalletBase.open( final wallet = await BitcoinWalletBase.open(
password: password, name: name, walletInfo: walletInfo, password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init();
saveBackup(name);
return wallet;
} catch (_) {
await restoreWalletFilesFromBackup(name);
final wallet = await BitcoinWalletBase.open(
password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource); unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init(); await wallet.init();
return wallet; return wallet;
} }
}
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())) File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
.delete(recursive: true); final walletInfo = walletInfoSource.values
final walletInfo = walletInfoSource.values.firstWhereOrNull( .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key); await walletInfoSource.delete(walletInfo.key);
} }
@override @override
Future<void> rename(String currentName, String password, String newName) async { Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( final currentWalletInfo = walletInfoSource.values
(info) => info.id == WalletBase.idFor(currentName, getType()))!; .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await BitcoinWalletBase.open( final currentWallet = await BitcoinWalletBase.open(
password: password, password: password,
name: currentName, name: currentName,
@ -71,6 +82,7 @@ class BitcoinWalletService extends WalletService<
unspentCoinsInfo: unspentCoinsInfoSource); unspentCoinsInfo: unspentCoinsInfoSource);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo; final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.id = WalletBase.idFor(newName, getType());
@ -80,13 +92,11 @@ class BitcoinWalletService extends WalletService<
} }
@override @override
Future<BitcoinWallet> restoreFromKeys( Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
throw UnimplementedError(); throw UnimplementedError();
@override @override
Future<BitcoinWallet> restoreFromSeed( Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
BitcoinRestoreWalletFromSeedCredentials credentials) async {
if (!validateMnemonic(credentials.mnemonic)) { if (!validateMnemonic(credentials.mnemonic)) {
throw BitcoinMnemonicIsIncorrectException(); throw BitcoinMnemonicIsIncorrectException();
} }

View file

@ -601,8 +601,6 @@ abstract class ElectrumWalletBase
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history})); electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
final historyResults = await Future.wait(histories); final historyResults = await Future.wait(histories);
historyResults.forEach((history) { historyResults.forEach((history) {
history.entries.forEach((historyItem) { history.entries.forEach((historyItem) {
if (historyItem.value.isNotEmpty) { if (historyItem.value.isNotEmpty) {
@ -622,7 +620,6 @@ abstract class ElectrumWalletBase
} }
} }
addressHashes.forEach((sh, addressRecord) { addressHashes.forEach((sh, addressRecord) {
addressRecord.txCount = newTxCounts[sh] ?? 0; addressRecord.txCount = newTxCounts[sh] ?? 0;
}); });

View file

@ -45,12 +45,23 @@ class LitecoinWalletService extends WalletService<
Future<LitecoinWallet> openWallet(String name, String password) async { Future<LitecoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(name, getType()))!; (info) => info.id == WalletBase.idFor(name, getType()))!;
try {
final wallet = await LitecoinWalletBase.open(
password: password, name: name, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init();
saveBackup(name);
return wallet;
} catch (_) {
await restoreWalletFilesFromBackup(name);
final wallet = await LitecoinWalletBase.open( final wallet = await LitecoinWalletBase.open(
password: password, name: name, walletInfo: walletInfo, password: password, name: name, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource); unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init(); await wallet.init();
return wallet; return wallet;
} }
}
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
@ -72,6 +83,7 @@ class LitecoinWalletService extends WalletService<
unspentCoinsInfo: unspentCoinsInfoSource); unspentCoinsInfo: unspentCoinsInfoSource);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo; final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.id = WalletBase.idFor(newName, getType());

View file

@ -51,12 +51,23 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
Future<BitcoinCashWallet> openWallet(String name, String password) async { Future<BitcoinCashWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull( final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(name, getType()))!; (info) => info.id == WalletBase.idFor(name, getType()))!;
try {
final wallet = await BitcoinCashWalletBase.open(
password: password, name: name, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init();
saveBackup(name);
return wallet;
} catch(_) {
await restoreWalletFilesFromBackup(name);
final wallet = await BitcoinCashWalletBase.open( final wallet = await BitcoinCashWalletBase.open(
password: password, name: name, walletInfo: walletInfo, password: password, name: name, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource); unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init(); await wallet.init();
return wallet; return wallet;
} }
}
@override @override
Future<void> remove(String wallet) async { Future<void> remove(String wallet) async {
@ -78,6 +89,7 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
unspentCoinsInfo: unspentCoinsInfoSource); unspentCoinsInfo: unspentCoinsInfoSource);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo; final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.id = WalletBase.idFor(newName, getType());

View file

@ -54,6 +54,17 @@ Future<void> restoreWalletFiles(String name) async {
} }
} }
Future<void> resetCache(String name) async {
await removeCache(name);
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
final cacheFilePath = '$walletDirPath/$name';
final backupCacheFile = File(backupFileName(cacheFilePath));
if (backupCacheFile.existsSync()) {
await backupCacheFile.copy(cacheFilePath);
}
}
Future<bool> backupWalletFilesExists(String name) async { Future<bool> backupWalletFilesExists(String name) async {
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero); final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
final cacheFilePath = '$walletDirPath/$name'; final cacheFilePath = '$walletDirPath/$name';
@ -63,9 +74,9 @@ Future<bool> backupWalletFilesExists(String name) async {
final backupKeysFile = File(backupFileName(keysFilePath)); final backupKeysFile = File(backupFileName(keysFilePath));
final backupAddressListFile = File(backupFileName(addressListFilePath)); final backupAddressListFile = File(backupFileName(addressListFilePath));
return backupCacheFile.existsSync() return backupCacheFile.existsSync() &&
&& backupKeysFile.existsSync() backupKeysFile.existsSync() &&
&& backupAddressListFile.existsSync(); backupAddressListFile.existsSync();
} }
Future<void> removeCache(String name) async { Future<void> removeCache(String name) async {

View file

@ -1,7 +1,8 @@
import 'package:cw_core/node.dart'; import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials, abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
@ -21,4 +22,22 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
Future<void> remove(String wallet); Future<void> remove(String wallet);
Future<void> rename(String currentName, String password, String newName); Future<void> rename(String currentName, String password, String newName);
Future<void> restoreWalletFilesFromBackup(String name) async {
final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType());
final walletDirPath = await pathForWalletDir(name: name, type: getType());
if (File(backupWalletDirPath).existsSync()) {
await File(backupWalletDirPath).copy(walletDirPath);
}
}
Future<void> saveBackup(String name) async {
final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType());
final walletDirPath = await pathForWalletDir(name: name, type: getType());
if (File(walletDirPath).existsSync()) {
await File(walletDirPath).copy(backupWalletDirPath);
}
}
} }

View file

@ -39,6 +39,8 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
Future<EthereumWallet> openWallet(String name, String password) async { Future<EthereumWallet> openWallet(String name, String password) async {
final walletInfo = final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType())); walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
try {
final wallet = await EthereumWallet.open( final wallet = await EthereumWallet.open(
name: name, name: name,
password: password, password: password,
@ -47,8 +49,21 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
await wallet.init(); await wallet.init();
await wallet.save(); await wallet.save();
saveBackup(name);
return wallet; return wallet;
} catch (_) {
await restoreWalletFilesFromBackup(name);
final wallet = await EthereumWallet.open(
name: name,
password: password,
walletInfo: walletInfo,
);
await wallet.init();
await wallet.save();
return wallet;
}
} }
@override @override
@ -59,6 +74,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
password: password, name: currentName, walletInfo: currentWalletInfo); password: password, name: currentName, walletInfo: currentWalletInfo);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo; final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.id = WalletBase.idFor(newName, getType());

View file

@ -163,6 +163,7 @@ class HavenWalletService extends WalletService<
final currentWallet = HavenWallet(walletInfo: currentWalletInfo); final currentWallet = HavenWallet(walletInfo: currentWalletInfo);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo; final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.id = WalletBase.idFor(newName, getType());

View file

@ -36,6 +36,8 @@ import 'package:mobx/mobx.dart';
part 'monero_wallet.g.dart'; part 'monero_wallet.g.dart';
const moneroBlockSize = 1000; const moneroBlockSize = 1000;
// not sure if this should just be 0 but setting it higher feels safer / should catch more cases:
const MIN_RESTORE_HEIGHT = 1000;
class MoneroWallet = MoneroWalletBase with _$MoneroWallet; class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
@ -79,7 +81,7 @@ abstract class MoneroWalletBase
Box<UnspentCoinsInfo> unspentCoinsInfo; Box<UnspentCoinsInfo> unspentCoinsInfo;
void Function(FlutterErrorDetails)? _onError; void Function(FlutterErrorDetails)? onError;
@override @override
late MoneroWalletAddresses walletAddresses; late MoneroWalletAddresses walletAddresses;
@ -171,7 +173,26 @@ abstract class MoneroWalletBase
Future<void> startSync() async { Future<void> startSync() async {
try { try {
_setInitialHeight(); _setInitialHeight();
} catch (_) {} } catch (_) {
// our restore height wasn't correct, so lets see if using the backup works:
try {
await resetCache(name);
_setInitialHeight();
} catch (e) {
// we still couldn't get a valid height from the backup?!:
// try to use the date instead:
try {
_setHeightFromDate();
} catch (e, s) {
// we still couldn't get a valid sync height :/
onError?.call(FlutterErrorDetails(
exception: e,
stack: s,
library: this.runtimeType.toString(),
));
}
}
}
try { try {
syncStatus = AttemptingSyncStatus(); syncStatus = AttemptingSyncStatus();
@ -339,6 +360,8 @@ abstract class MoneroWalletBase
if (currentAddressListFile.existsSync()) { if (currentAddressListFile.existsSync()) {
await currentAddressListFile.rename('$newWalletPath.address.txt'); await currentAddressListFile.rename('$newWalletPath.address.txt');
} }
await backupWalletFiles(newWalletName);
} catch (e) { } catch (e) {
final currentWalletPath = await pathForWallet(name: name, type: type); final currentWalletPath = await pathForWallet(name: name, type: type);
@ -402,9 +425,7 @@ abstract class MoneroWalletBase
if (coin.spent == 0) { if (coin.spent == 0) {
final unspent = MoneroUnspent.fromCoinsInfoRow(coin); final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
if (unspent.hash.isNotEmpty) { if (unspent.hash.isNotEmpty) {
unspent.isChange = transaction_history unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1;
.getTransaction(unspent.hash)
.direction == 1;
} }
unspentCoins.add(unspent); unspentCoins.add(unspent);
} }
@ -438,7 +459,7 @@ abstract class MoneroWalletBase
_askForUpdateBalance(); _askForUpdateBalance();
} catch (e, s) { } catch (e, s) {
print(e.toString()); print(e.toString());
_onError?.call(FlutterErrorDetails( onError?.call(FlutterErrorDetails(
exception: e, exception: e,
stack: s, stack: s,
library: this.runtimeType.toString(), library: this.runtimeType.toString(),
@ -534,18 +555,36 @@ abstract class MoneroWalletBase
_listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction); _listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction);
} }
// check if the height is correct:
void _setInitialHeight() { void _setInitialHeight() {
if (walletInfo.isRecovery) { if (walletInfo.isRecovery) {
return; return;
} }
final currentHeight = monero_wallet.getCurrentHeight(); final height = monero_wallet.getCurrentHeight();
if (height > MIN_RESTORE_HEIGHT) {
// the restore height is probably correct, so we do nothing:
return;
}
throw Exception("height isn't > $MIN_RESTORE_HEIGHT!");
}
void _setHeightFromDate() {
if (walletInfo.isRecovery) {
return;
}
if (currentHeight <= 1) {
final height = _getHeightByDate(walletInfo.date); final height = _getHeightByDate(walletInfo.date);
if (height > MIN_RESTORE_HEIGHT) {
monero_wallet.setRecoveringFromSeed(isRecovery: true); monero_wallet.setRecoveringFromSeed(isRecovery: true);
monero_wallet.setRefreshFromBlockHeight(height: height); monero_wallet.setRefreshFromBlockHeight(height: height);
return;
} }
throw Exception("height isn't > $MIN_RESTORE_HEIGHT!");
} }
int _getHeightDistance(DateTime date) { int _getHeightDistance(DateTime date) {
@ -561,7 +600,8 @@ abstract class MoneroWalletBase
final heightDistance = _getHeightDistance(date); final heightDistance = _getHeightDistance(date);
if (nodeHeight <= 0) { if (nodeHeight <= 0) {
return 0; // the node returned 0 (an error state), so lets just restore from cache:
throw Exception("nodeHeight is <= 0!");
} }
return nodeHeight - heightDistance; return nodeHeight - heightDistance;
@ -650,7 +690,7 @@ abstract class MoneroWalletBase
} }
@override @override
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError; void setExceptionHandler(void Function(FlutterErrorDetails) e) => onError = e;
@override @override
String signMessage(String message, {String? address}) { String signMessage(String message, {String? address}) {

View file

@ -11,11 +11,13 @@ import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager; import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
import 'package:cw_monero/monero_wallet.dart'; import 'package:cw_monero/monero_wallet.dart';
import 'package:flutter/widgets.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:polyseed/polyseed.dart'; import 'package:polyseed/polyseed.dart';
class MoneroNewWalletCredentials extends WalletCredentials { class MoneroNewWalletCredentials extends WalletCredentials {
MoneroNewWalletCredentials({required String name, required this.language, required this.isPolyseed, String? password}) MoneroNewWalletCredentials(
{required String name, required this.language, required this.isPolyseed, String? password})
: super(name: name, password: password); : super(name: name, password: password);
final String language; final String language;
@ -52,10 +54,8 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
final String spendKey; final String spendKey;
} }
class MoneroWalletService extends WalletService< class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
MoneroNewWalletCredentials, MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials> {
MoneroRestoreWalletFromSeedCredentials,
MoneroRestoreWalletFromKeysCredentials> {
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
@ -112,6 +112,7 @@ class MoneroWalletService extends WalletService<
@override @override
Future<MoneroWallet> openWallet(String name, String password) async { Future<MoneroWallet> openWallet(String name, String password) async {
MoneroWallet? wallet;
try { try {
final path = await pathForWallet(name: name, type: getType()); final path = await pathForWallet(name: name, type: getType());
@ -119,11 +120,10 @@ class MoneroWalletService extends WalletService<
await repairOldAndroidWallet(name); await repairOldAndroidWallet(name);
} }
await monero_wallet_manager await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
.openWalletAsync({'path': path, 'password': password}); final walletInfo = walletInfoSource.values
final walletInfo = walletInfoSource.values.firstWhere( .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
(info) => info.id == WalletBase.idFor(name, getType())); wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
final isValid = wallet.walletAddresses.validate(); final isValid = wallet.walletAddresses.validate();
if (!isValid) { if (!isValid) {
@ -135,7 +135,7 @@ class MoneroWalletService extends WalletService<
await wallet.init(); await wallet.init();
return wallet; return wallet;
} catch (e) { } catch (e, s) {
// TODO: Implement Exception for wallet list service. // TODO: Implement Exception for wallet list service.
final bool isBadAlloc = e.toString().contains('bad_alloc') || final bool isBadAlloc = e.toString().contains('bad_alloc') ||
@ -156,16 +156,18 @@ class MoneroWalletService extends WalletService<
final bool invalidSignature = e.toString().contains('invalid signature') || final bool invalidSignature = e.toString().contains('invalid signature') ||
(e is WalletOpeningException && e.message.contains('invalid signature')); (e is WalletOpeningException && e.message.contains('invalid signature'));
if (isBadAlloc || if (!isBadAlloc &&
doesNotCorrespond || !doesNotCorrespond &&
isMissingCacheFilesIOS || !isMissingCacheFilesIOS &&
isMissingCacheFilesAndroid || !isMissingCacheFilesAndroid &&
invalidSignature) { !invalidSignature &&
await restoreOrResetWalletFiles(name); wallet != null &&
return openWallet(name, password); wallet.onError != null) {
wallet.onError!(FlutterErrorDetails(exception: e, stack: s));
} }
rethrow; await restoreOrResetWalletFiles(name);
return openWallet(name, password);
} }
} }
@ -185,10 +187,9 @@ class MoneroWalletService extends WalletService<
} }
@override @override
Future<void> rename( Future<void> rename(String currentName, String password, String newName) async {
String currentName, String password, String newName) async { final currentWalletInfo = walletInfoSource.values
final currentWalletInfo = walletInfoSource.values.firstWhere( .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
(info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = final currentWallet =
MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource); MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
@ -202,8 +203,7 @@ class MoneroWalletService extends WalletService<
} }
@override @override
Future<MoneroWallet> restoreFromKeys( Future<MoneroWallet> restoreFromKeys(MoneroRestoreWalletFromKeysCredentials credentials) async {
MoneroRestoreWalletFromKeysCredentials credentials) async {
try { try {
final path = await pathForWallet(name: credentials.name, type: getType()); final path = await pathForWallet(name: credentials.name, type: getType());
await monero_wallet_manager.restoreFromKeys( await monero_wallet_manager.restoreFromKeys(
@ -227,9 +227,7 @@ class MoneroWalletService extends WalletService<
} }
@override @override
Future<MoneroWallet> restoreFromSeed( Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials) async {
MoneroRestoreWalletFromSeedCredentials credentials) async {
// Restore from Polyseed // Restore from Polyseed
if (Polyseed.isValidSeed(credentials.mnemonic)) { if (Polyseed.isValidSeed(credentials.mnemonic)) {
return restoreFromPolyseed(credentials); return restoreFromPolyseed(credentials);
@ -254,14 +252,16 @@ class MoneroWalletService extends WalletService<
} }
} }
Future<MoneroWallet> restoreFromPolyseed(MoneroRestoreWalletFromSeedCredentials credentials) async { Future<MoneroWallet> restoreFromPolyseed(
MoneroRestoreWalletFromSeedCredentials credentials) async {
try { try {
final path = await pathForWallet(name: credentials.name, type: getType()); final path = await pathForWallet(name: credentials.name, type: getType());
final polyseedCoin = PolyseedCoin.POLYSEED_MONERO; final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
final lang = PolyseedLang.getByPhrase(credentials.mnemonic); final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin); final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
return _restoreFromPolyseed(path, credentials.password!, polyseed, credentials.walletInfo!, lang); return _restoreFromPolyseed(
path, credentials.password!, polyseed, credentials.walletInfo!, lang);
} catch (e) { } catch (e) {
// TODO: Implement Exception for wallet list service. // TODO: Implement Exception for wallet list service.
print('MoneroWalletsManager Error: $e'); print('MoneroWalletsManager Error: $e');
@ -269,11 +269,11 @@ class MoneroWalletService extends WalletService<
} }
} }
Future<MoneroWallet> _restoreFromPolyseed(String path, String password, Polyseed polyseed, Future<MoneroWallet> _restoreFromPolyseed(
WalletInfo walletInfo, PolyseedLang lang, String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async { {PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
final height = overrideHeight ?? getMoneroHeigthByDate( final height = overrideHeight ??
date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000)); getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
final spendKey = polyseed.generateKey(coin, 32).toHexString(); final spendKey = polyseed.generateKey(coin, 32).toHexString();
final seed = polyseed.encode(lang, coin); final seed = polyseed.encode(lang, coin);
@ -288,8 +288,7 @@ class MoneroWalletService extends WalletService<
restoreHeight: height, restoreHeight: height,
spendKey: spendKey); spendKey: spendKey);
final wallet = MoneroWallet( final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init(); await wallet.init();
return wallet; return wallet;
@ -301,16 +300,14 @@ class MoneroWalletService extends WalletService<
return; return;
} }
final oldAndroidWalletDirPath = final oldAndroidWalletDirPath = await outdatedAndroidPathForWalletDir(name: name);
await outdatedAndroidPathForWalletDir(name: name);
final dir = Directory(oldAndroidWalletDirPath); final dir = Directory(oldAndroidWalletDirPath);
if (!dir.existsSync()) { if (!dir.existsSync()) {
return; return;
} }
final newWalletDirPath = final newWalletDirPath = await pathForWalletDir(name: name, type: getType());
await pathForWalletDir(name: name, type: getType());
dir.listSync().forEach((f) { dir.listSync().forEach((f) {
final file = File(f.path); final file = File(f.path);

View file

@ -69,6 +69,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords); NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo; final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.id = WalletBase.idFor(newName, getType());
@ -150,6 +151,20 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
Future<NanoWallet> openWallet(String name, String password) async { Future<NanoWallet> openWallet(String name, String password) async {
final walletInfo = final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType())); walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
try {
final wallet = await NanoWalletBase.open(
name: name,
password: password,
walletInfo: walletInfo,
);
await wallet.init();
await wallet.save();
saveBackup(name);
return wallet;
} catch (_) {
await restoreWalletFilesFromBackup(name);
final wallet = await NanoWalletBase.open( final wallet = await NanoWalletBase.open(
name: name, name: name,
password: password, password: password,
@ -161,3 +176,4 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
return wallet; return wallet;
} }
} }
}

View file

@ -42,6 +42,8 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
Future<PolygonWallet> openWallet(String name, String password) async { Future<PolygonWallet> openWallet(String name, String password) async {
final walletInfo = final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType())); walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
try {
final wallet = await PolygonWallet.open( final wallet = await PolygonWallet.open(
name: name, name: name,
password: password, password: password,
@ -50,8 +52,21 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
await wallet.init(); await wallet.init();
await wallet.save(); await wallet.save();
saveBackup(name);
return wallet; return wallet;
} catch (_) {
await restoreWalletFilesFromBackup(name);
final wallet = await PolygonWallet.open(
name: name,
password: password,
walletInfo: walletInfo,
);
await wallet.init();
await wallet.save();
return wallet;
}
} }
@override @override
@ -100,6 +115,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
password: password, name: currentName, walletInfo: currentWalletInfo); password: password, name: currentName, walletInfo: currentWalletInfo);
await currentWallet.renameWalletFiles(newName); await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
final newWalletInfo = currentWalletInfo; final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); newWalletInfo.id = WalletBase.idFor(newName, getType());