mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
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:
parent
e4ddf82e69
commit
26fe28891d
12 changed files with 264 additions and 117 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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}) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -160,4 +175,5 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
await wallet.save();
|
await wallet.save();
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
Loading…
Reference in a new issue