Restore wallet WIP

This commit is contained in:
Blazebrain 2023-06-13 10:03:17 +01:00
parent 6d7a384a98
commit 9ea0caea64
4 changed files with 173 additions and 133 deletions

1
.gitignore vendored
View file

@ -8,6 +8,7 @@
.buildlog/
.history
.svn/
.fvm/
# IntelliJ related
*.iml

View file

@ -28,7 +28,8 @@ final transactionCreateNative = moneroApi
.asFunction<TransactionCreate>();
final transactionCreateMultDestNative = moneroApi
.lookup<NativeFunction<transaction_create_mult_dest>>('transaction_create_mult_dest')
.lookup<NativeFunction<transaction_create_mult_dest>>(
'transaction_create_mult_dest')
.asFunction<TransactionCreateMultDest>();
final transactionCommitNative = moneroApi
@ -97,6 +98,7 @@ PendingTransactionDescription createTransactionSync(
if (!created) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
print('Error message from creating transaction call $message');
throw CreationTransactionException(message: message);
}
@ -111,15 +113,15 @@ PendingTransactionDescription createTransactionSync(
PendingTransactionDescription createTransactionMultDestSync(
{required List<MoneroOutput> outputs,
required String paymentId,
required int priorityRaw,
int accountIndex = 0}) {
required String paymentId,
required int priorityRaw,
int accountIndex = 0}) {
final int size = outputs.length;
final List<Pointer<Utf8>> addressesPointers = outputs.map((output) =>
output.address.toNativeUtf8()).toList();
final List<Pointer<Utf8>> addressesPointers =
outputs.map((output) => output.address.toNativeUtf8()).toList();
final Pointer<Pointer<Utf8>> addressesPointerPointer = calloc(size);
final List<Pointer<Utf8>> amountsPointers = outputs.map((output) =>
output.amount.toNativeUtf8()).toList();
final List<Pointer<Utf8>> amountsPointers =
outputs.map((output) => output.amount.toNativeUtf8()).toList();
final Pointer<Pointer<Utf8>> amountsPointerPointer = calloc(size);
for (int i = 0; i < size; i++) {
@ -131,14 +133,14 @@ PendingTransactionDescription createTransactionMultDestSync(
final errorMessagePointer = calloc<Utf8Box>();
final pendingTransactionRawPointer = calloc<PendingTransactionRaw>();
final created = transactionCreateMultDestNative(
addressesPointerPointer,
paymentIdPointer,
amountsPointerPointer,
size,
priorityRaw,
accountIndex,
errorMessagePointer,
pendingTransactionRawPointer) !=
addressesPointerPointer,
paymentIdPointer,
amountsPointerPointer,
size,
priorityRaw,
accountIndex,
errorMessagePointer,
pendingTransactionRawPointer) !=
0;
calloc.free(addressesPointerPointer);
@ -164,10 +166,13 @@ PendingTransactionDescription createTransactionMultDestSync(
pointerAddress: pendingTransactionRawPointer.address);
}
void commitTransactionFromPointerAddress({required int address}) => commitTransaction(
transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address));
void commitTransactionFromPointerAddress({required int address}) =>
commitTransaction(
transactionPointer:
Pointer<PendingTransactionRaw>.fromAddress(address));
void commitTransaction({required Pointer<PendingTransactionRaw> transactionPointer}) {
void commitTransaction(
{required Pointer<PendingTransactionRaw> transactionPointer}) {
final errorMessagePointer = calloc<Utf8Box>();
final isCommited =
transactionCommitNative(transactionPointer, errorMessagePointer) != 0;
@ -222,10 +227,10 @@ Future<PendingTransactionDescription> createTransaction(
});
Future<PendingTransactionDescription> createTransactionMultDest(
{required List<MoneroOutput> outputs,
required int priorityRaw,
String paymentId = '',
int accountIndex = 0}) =>
{required List<MoneroOutput> outputs,
required int priorityRaw,
String paymentId = '',
int accountIndex = 0}) =>
compute(_createTransactionMultDestSync, {
'outputs': outputs,
'paymentId': paymentId,

View file

@ -38,29 +38,29 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
MoneroTransactionHistory, MoneroTransactionInfo> with Store {
MoneroWalletBase({required WalletInfo walletInfo})
: balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({
CryptoCurrency.xmr: MoneroBalance(
CryptoCurrency.xmr: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0))
}),
}),
_isTransactionUpdating = false,
_hasSyncAfterStartup = false,
walletAddresses = MoneroWalletAddresses(walletInfo),
syncStatus = NotConnectedSyncStatus(),
super(walletInfo) {
transactionHistory = MoneroTransactionHistory();
_onAccountChangeReaction = reaction((_) => walletAddresses.account,
(Account? account) {
_onAccountChangeReaction =
reaction((_) => walletAddresses.account, (Account? account) {
if (account == null) {
return;
}
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(
<CryptoCurrency, MoneroBalance>{
currency: MoneroBalance(
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency,
MoneroBalance>{
currency: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
unlockedBalance:
monero_wallet.getUnlockedBalance(accountIndex: account.id))
});
});
walletAddresses.updateSubaddressList(accountIndex: account.id);
});
}
@ -96,12 +96,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
Future<void> init() async {
await walletAddresses.init();
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(
<CryptoCurrency, MoneroBalance>{
currency: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id),
unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id))
});
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency,
MoneroBalance>{
currency: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(
accountIndex: walletAddresses.account!.id),
unlockedBalance: monero_wallet.getUnlockedBalance(
accountIndex: walletAddresses.account!.id))
});
_setListeners();
await updateTransactions();
@ -115,9 +117,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
}
_autoSaveTimer = Timer.periodic(
Duration(seconds: _autoSaveInterval),
(_) async => await save());
Duration(seconds: _autoSaveInterval), (_) async => await save());
}
@override
Future<void>? updateBalance() => null;
@ -170,69 +172,68 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final _credentials = credentials as MoneroTransactionCreationCredentials;
final outputs = _credentials.outputs;
final hasMultiDestination = outputs.length > 1;
final unlockedBalance =
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
final unlockedBalance = monero_wallet.getUnlockedBalance(
accountIndex: walletAddresses.account!.id);
PendingTransactionDescription pendingTransactionDescription;
// if (!(syncStatus is SyncedSyncStatus)) {
// print('Wallet is not synced');
// throw MoneroTransactionCreationException('The wallet is not synced.');
// }
if (hasMultiDestination) {
if (outputs.any((item) => item.sendAll
|| (item.formattedCryptoAmount ?? 0) <= 0)) {
throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
if (outputs.any(
(item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
throw MoneroTransactionCreationException(
'You do not have enough XMR to send this amount.');
}
final int totalAmount = outputs.fold(0, (acc, value) =>
acc + (value.formattedCryptoAmount ?? 0));
final int totalAmount = outputs.fold(
0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
if (unlockedBalance < totalAmount) {
throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
throw MoneroTransactionCreationException(
'You do not have enough XMR to send this amount.');
}
final moneroOutputs = outputs.map((output) {
final outputAddress = output.isParsedAddress
? output.extractedAddress
: output.address;
final outputAddress =
output.isParsedAddress ? output.extractedAddress : output.address;
return MoneroOutput(
address: outputAddress!,
amount: output.cryptoAmount!.replaceAll(',', '.'));
return MoneroOutput(
address: outputAddress!,
amount: output.cryptoAmount!.replaceAll(',', '.'));
}).toList();
pendingTransactionDescription =
await transaction_history.createTransactionMultDest(
outputs: moneroOutputs,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account!.id);
await transaction_history.createTransactionMultDest(
outputs: moneroOutputs,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account!.id);
} else {
final output = outputs.first;
final address = output.isParsedAddress
? output.extractedAddress
: output.address;
final amount = output.sendAll
? null
: output.cryptoAmount!.replaceAll(',', '.');
final formattedAmount = output.sendAll
? null
: output.formattedCryptoAmount;
final address =
output.isParsedAddress ? output.extractedAddress : output.address;
final amount =
output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.');
final formattedAmount =
output.sendAll ? null : output.formattedCryptoAmount;
if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
(formattedAmount == null && unlockedBalance <= 0)) {
final formattedBalance = moneroAmountToString(amount: unlockedBalance);
// if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
// (formattedAmount == null && unlockedBalance <= 0)) {
// final formattedBalance = moneroAmountToString(amount: unlockedBalance);
throw MoneroTransactionCreationException(
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
}
// throw MoneroTransactionCreationException(
// 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
// }
pendingTransactionDescription =
await transaction_history.createTransaction(
address: address!,
amount: amount,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account!.id);
await transaction_history.createTransaction(
address: address!,
amount: amount,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account!.id);
}
return PendingMoneroTransaction(pendingTransactionDescription);
@ -297,8 +298,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
String getTransactionAddress(int accountIndex, int addressIndex) =>
monero_wallet.getAddress(
accountIndex: accountIndex,
addressIndex: addressIndex);
accountIndex: accountIndex, addressIndex: addressIndex);
@override
Future<Map<String, MoneroTransactionInfo>> fetchTransactions() async {
@ -394,8 +394,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
int _getFullBalance() =>
monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id);
int _getUnlockedBalance() =>
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
int _getUnlockedBalance() => monero_wallet.getUnlockedBalance(
accountIndex: walletAddresses.account!.id);
void _onNewBlock(int height, int blocksLeft, double ptc) async {
try {
@ -412,9 +412,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
syncStatus = SyncedSyncStatus();
if (!_hasSyncAfterStartup) {
_hasSyncAfterStartup = true;
await save();
}
_hasSyncAfterStartup = true;
await save();
}
if (walletInfo.isRecovery) {
await setAsRecovered();

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/balance.dart';
@ -27,7 +28,8 @@ part 'wallet_creation_vm.g.dart';
class WalletCreationVM = WalletCreationVMBase with _$WalletCreationVM;
abstract class WalletCreationVMBase with Store {
WalletCreationVMBase(this._appStore, this._walletInfoSource, this.walletCreationService, this._fiatConversationStore,
WalletCreationVMBase(this._appStore, this._walletInfoSource,
this.walletCreationService, this._fiatConversationStore,
{required this.type, required this.isRecovery})
: state = InitialExecutionState(),
name = '';
@ -46,9 +48,9 @@ abstract class WalletCreationVMBase with Store {
final WalletCreationService walletCreationService;
final Box<WalletInfo> _walletInfoSource;
final AppStore _appStore;
final FiatConversionStore _fiatConversationStore;
final FiatConversionStore _fiatConversationStore;
Completer<void> syncCompleter = Completer<void>();
Completer<void> syncCompleter = Completer<void>();
bool nameExists(String name) => walletCreationService.exists(name);
@ -58,13 +60,16 @@ abstract class WalletCreationVMBase with Store {
final type = restoreWallet?.type ?? this.type;
try {
//! Create a restoredWallet from the scanned wallet parameters
final restoredWallet =
await createNewWalletWithoutSwitching(options: options, restoreWallet: restoreWallet);
print('Restored Wallet Address ' + restoredWallet.walletAddresses.address);
final restoredWallet = await createNewWalletWithoutSwitching(
options: options, restoreWallet: restoreWallet);
print(
'Restored Wallet Address ' + restoredWallet.walletAddresses.address);
//TODO Get transactions details to verify 10 confirmations
//! Create the newWallet that will received the funds
// if (restoreWallet != null &&
// restoreWallet.restoreMode == WalletRestoreMode.txids) {
//* Create the newWallet that will received the funds
final newWallet = await createNewWalletWithoutSwitching(
options: options,
regenerateName: true,
@ -72,31 +77,36 @@ abstract class WalletCreationVMBase with Store {
final newWalletAddress = newWallet.walletAddresses.address;
print('New Wallet Address ' + newWalletAddress);
//! Switch to the restoredWallet in order to activate the node connection
//* Switch to the restoredWallet in order to activate the node connection
_appStore.changeCurrentWallet(restoredWallet);
await restoredWallet.startSync();
//! Sweep all funds from restoredWallet to newWallet
await sweepAllFundsToNewWallet(restoredWallet, type, newWalletAddress, restoreWallet?.txId ?? '');
//! Switch back to new wallet
_appStore.changeCurrentWallet(newWallet);
//! Add the new Wallet info to the walletInfoSource
await _walletInfoSource.add(newWallet.walletInfo);
//! Approve authentication as successful
_appStore.authenticationStore.allowed();
state = ExecutedSuccessfullyState();
//* Sweep all funds from restoredWallet to newWallet
await sweepAllFundsToNewWallet(
restoredWallet,
type,
newWalletAddress,
restoreWallet?.txId ?? '',
);
// } else {
// await _walletInfoSource.add(restoredWallet.walletInfo);
// _appStore.changeCurrentWallet(restoredWallet);
// _appStore.authenticationStore.allowed();
// state = ExecutedSuccessfullyState();
// }
} catch (e) {
print('Errorrrrr');
state = FailureState(e.toString());
}
}
Future<WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>>
createNewWalletWithoutSwitching(
{dynamic options, RestoredWallet? restoreWallet, bool regenerateName = false}) async {
Future<
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
TransactionInfo>> createNewWalletWithoutSwitching(
{dynamic options,
RestoredWallet? restoreWallet,
bool regenerateName = false}) async {
state = IsExecutingState();
if (name.isEmpty) {
name = await generateName();
@ -122,35 +132,55 @@ abstract class WalletCreationVMBase with Store {
path: path,
dirPath: dirPath,
address: '',
showIntroCakePayCard:
(!walletCreationService.typeExists(type)) && type != WalletType.haven);
showIntroCakePayCard: (!walletCreationService.typeExists(type)) &&
type != WalletType.haven);
credentials.walletInfo = walletInfo;
final wallet = restoreWallet != null
? await processFromRestoredWallet(credentials, restoreWallet)
: await process(credentials);
walletInfo.address = wallet.walletAddresses.address;
return wallet;
}
Future<void> sweepAllFundsToNewWallet(WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet,
WalletType type, String newWalletAddress, String paymentId) async {
final output = Output(wallet, _appStore.settingsStore, _fiatConversationStore, () => wallet.currency);
output.address = newWalletAddress;
output.sendAll = true;
output.note = 'testing the sweep all function';
final credentials = _credentials(type, wallet.currency.title, output);
print('About to enter create function');
await createTransaction(wallet, credentials);
// final currentNode = _appStore.settingsStore.getCurrentNode(type);
Future<void> sweepAllFundsToNewWallet(
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
TransactionInfo>
wallet,
WalletType type,
String newWalletAddress,
String paymentId) async {
final output = Output(wallet, _appStore.settingsStore,
_fiatConversationStore, () => wallet.currency);
output.address = newWalletAddress;
output.sendAll = true;
output.note = 'testing the sweep all function';
final credentials = _credentials(type, wallet.currency.title, output);
print('About to enter create function');
try {
await createTransaction(wallet, credentials);
// final currentNode = _appStore.settingsStore.getCurrentNode(type);
// final result = await walletCreationService.sweepAllFunds(currentNode, newWalletAddress, paymentId);
//* Switch back to new wallet
_appStore.changeCurrentWallet(wallet);
//* Add the new Wallet info to the walletInfoSource
await _walletInfoSource.add(wallet.walletInfo);
//* Approve authentication as successful
_appStore.authenticationStore.allowed();
print('Successfully done inisde sweep all');
state = ExecutedSuccessfullyState();
} catch (e) {
state = FailureState(e.toString());
}
}
Object _credentials(WalletType type,String cryptoCurrencyTitle,Output output ) {
Object _credentials(
WalletType type, String cryptoCurrencyTitle, Output output) {
switch (type) {
case WalletType.bitcoin:
final priority = _appStore.settingsStore.priority[type];
@ -159,7 +189,8 @@ abstract class WalletCreationVMBase with Store {
throw Exception('Priority is null for wallet type: ${type}');
}
return bitcoin!.createBitcoinTransactionCredentials([output], priority: priority);
return bitcoin!
.createBitcoinTransactionCredentials([output], priority: priority);
case WalletType.litecoin:
final priority = _appStore.settingsStore.priority[type];
@ -167,7 +198,8 @@ abstract class WalletCreationVMBase with Store {
throw Exception('Priority is null for wallet type: ${type}');
}
return bitcoin!.createBitcoinTransactionCredentials([output], priority: priority);
return bitcoin!
.createBitcoinTransactionCredentials([output], priority: priority);
case WalletType.monero:
final priority = _appStore.settingsStore.priority[type];
@ -176,16 +208,18 @@ abstract class WalletCreationVMBase with Store {
}
return monero!.createMoneroTransactionCreationCredentials(
outputs:[output], priority: priority);
outputs: [output], priority: priority);
case WalletType.haven:
final priority = _appStore.settingsStore.priority[type];
if (priority == null) {
throw Exception('Priority is null for wallet type: ${type}');
}
return haven!.createHavenTransactionCreationCredentials(
outputs: [output], priority: priority, assetType: cryptoCurrencyTitle);
outputs: [output],
priority: priority,
assetType: cryptoCurrencyTitle);
default:
throw Exception('Unexpected wallet type: ${type}');
}
@ -194,7 +228,7 @@ abstract class WalletCreationVMBase with Store {
@action
Future<void> createTransaction(WalletBase wallet, Object credentials) async {
try {
print('in here');
print('in here');
state = IsExecutingState();
print('about to enter wallet create transaction function');
pendingTransaction = await wallet.createTransaction(credentials);
@ -207,15 +241,15 @@ abstract class WalletCreationVMBase with Store {
WalletCredentials getCredentials(dynamic options) {
switch (type) {
case WalletType.monero:
return monero!
.createMoneroNewWalletCredentials(name: name, language: options as String? ?? '');
return monero!.createMoneroNewWalletCredentials(
name: name, language: options as String? ?? '');
case WalletType.bitcoin:
return bitcoin!.createBitcoinNewWalletCredentials(name: name);
case WalletType.litecoin:
return bitcoin!.createBitcoinNewWalletCredentials(name: name);
case WalletType.haven:
return haven!
.createHavenNewWalletCredentials(name: name, language: options as String? ?? '');
return haven!.createHavenNewWalletCredentials(
name: name, language: options as String? ?? '');
default:
throw Exception('Unexpected type: ${type.toString()}');
}