From a2294c4a061c40194223600a12aabacca3b15bc4 Mon Sep 17 00:00:00 2001 From: cyan Date: Fri, 2 May 2025 14:30:39 +0200 Subject: [PATCH] fix(cw_monero): prevent monero wallet from breaking during rename (#2214) * fix(cw_monero): prevent monero wallet from breaking during rename * update to cleaned up monero.dart * fix: transaction screen not refreshing in monero * fix: wallets not opening until app restart after rename. * fix(cw_decred): wallet renaming throwing * fix: transaction not being shown after sending until 1st confirmation * fix(cw_monero): loop safeguard * fix: don't await wallet.fetchTransactions --- cw_decred/lib/wallet.dart | 21 +- cw_decred/lib/wallet_service.dart | 4 + cw_monero/lib/api/account_list.dart | 67 ++-- cw_monero/lib/api/coins_info.dart | 19 +- cw_monero/lib/api/subaddress_list.dart | 68 ++-- cw_monero/lib/api/transaction_history.dart | 193 +++++----- cw_monero/lib/api/wallet.dart | 157 ++++---- cw_monero/lib/api/wallet_manager.dart | 339 ++++++------------ cw_monero/lib/ledger.dart | 16 +- cw_monero/lib/monero_account_list.dart | 28 +- cw_monero/lib/monero_unspent.dart | 2 +- cw_monero/lib/monero_wallet.dart | 171 +++++---- cw_monero/lib/monero_wallet_service.dart | 57 +-- cw_monero/pubspec.lock | 4 +- cw_monero/pubspec.yaml | 2 +- cw_wownero/pubspec.lock | 4 +- cw_wownero/pubspec.yaml | 2 +- cw_zano/pubspec.lock | 4 +- cw_zano/pubspec.yaml | 2 +- ios/Podfile.lock | 56 ++- lib/core/wallet_loading_service.dart | 33 +- lib/monero/cw_monero.dart | 4 +- .../dashboard/dashboard_view_model.dart | 32 +- lib/view_model/send/send_view_model.dart | 4 +- scripts/prepare_moneroc.sh | 2 +- 25 files changed, 577 insertions(+), 714 deletions(-) diff --git a/cw_decred/lib/wallet.dart b/cw_decred/lib/wallet.dart index a63a5e2e5..ac70a4aaa 100644 --- a/cw_decred/lib/wallet.dart +++ b/cw_decred/lib/wallet.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:path/path.dart' as p; import 'package:cw_core/exceptions.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/utils/print_verbose.dart'; @@ -602,7 +603,25 @@ abstract class DecredWalletBase throw "wallet already exists at $newDirPath"; } - await Directory(currentDirPath).rename(newDirPath); + final sourceDir = Directory(currentDirPath); + final targetDir = Directory(newDirPath); + + if (!targetDir.existsSync()) { + await targetDir.create(recursive: true); + } + + await for (final entity in sourceDir.list(recursive: true)) { + final relativePath = entity.path.substring(sourceDir.path.length+1); + final targetPath = p.join(targetDir.path, relativePath); + + if (entity is File) { + await entity.rename(targetPath); + } else if (entity is Directory) { + await Directory(targetPath).create(recursive: true); + } + } + + await sourceDir.delete(recursive: true); } @override diff --git a/cw_decred/lib/wallet_service.dart b/cw_decred/lib/wallet_service.dart index 161184b0a..e2313904e 100644 --- a/cw_decred/lib/wallet_service.dart +++ b/cw_decred/lib/wallet_service.dart @@ -118,6 +118,10 @@ class DecredWalletService extends WalletService< currentWalletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet ? testnet : mainnet; + if (libwallet == null) { + libwallet = await Libwallet.spawn(); + libwallet!.initLibdcrwallet("", "err"); + } final currentWallet = DecredWallet( currentWalletInfo, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet); diff --git a/cw_monero/lib/api/account_list.dart b/cw_monero/lib/api/account_list.dart index 3ceef5815..7f6a3f1aa 100644 --- a/cw_monero/lib/api/account_list.dart +++ b/cw_monero/lib/api/account_list.dart @@ -2,31 +2,31 @@ import 'dart:async'; import 'package:cw_monero/api/wallet.dart'; import 'package:cw_monero/monero_account_list.dart'; -import 'package:monero/monero.dart' as monero; +import 'package:monero/src/wallet2.dart'; +import 'package:monero/src/monero.dart'; -monero.wallet? wptr = null; -bool get isViewOnly => int.tryParse(monero.Wallet_secretSpendKey(wptr!)) == 0; +Wallet2Wallet? currentWallet = null; +bool get isViewOnly => int.tryParse(currentWallet!.secretSpendKey()) == 0; int _wlptrForW = 0; -monero.WalletListener? _wlptr = null; +Wallet2WalletListener? _wlptr = null; -monero.WalletListener? getWlptr() { - if (wptr == null) return null; - if (wptr!.address == _wlptrForW) return _wlptr!; - _wlptrForW = wptr!.address; - _wlptr = monero.MONERO_cw_getWalletListener(wptr!); +Wallet2WalletListener? getWlptr() { + if (currentWallet == null) return null; + _wlptrForW = currentWallet!.ffiAddress(); + _wlptr = currentWallet!.getWalletListener(); return _wlptr!; } -monero.SubaddressAccount? subaddressAccount; +Wallet2SubaddressAccount? subaddressAccount; bool isUpdating = false; void refreshAccounts() { try { isUpdating = true; - subaddressAccount = monero.Wallet_subaddressAccount(wptr!); - monero.SubaddressAccount_refresh(subaddressAccount!); + subaddressAccount = currentWallet!.subaddressAccount(); + subaddressAccount!.refresh(); isUpdating = false; } catch (e) { isUpdating = false; @@ -34,45 +34,28 @@ void refreshAccounts() { } } -List getAllAccount() { + List getAllAccount() { // final size = monero.Wallet_numSubaddressAccounts(wptr!); refreshAccounts(); - int size = monero.SubaddressAccount_getAll_size(subaddressAccount!); + int size = subaddressAccount!.getAll_size(); if (size == 0) { - monero.Wallet_addSubaddressAccount(wptr!); - monero.Wallet_status(wptr!); + currentWallet!.addSubaddressAccount(); + currentWallet!.status(); return []; } return List.generate(size, (index) { - return monero.SubaddressAccount_getAll_byIndex(subaddressAccount!, index: index); + return subaddressAccount!.getAll_byIndex(index); }); } -void addAccountSync({required String label}) { - monero.Wallet_addSubaddressAccount(wptr!, label: label); -} - -void setLabelForAccountSync({required int accountIndex, required String label}) { - monero.SubaddressAccount_setLabel(subaddressAccount!, accountIndex: accountIndex, label: label); - MoneroAccountListBase.cachedAccounts[wptr!.address] = []; - refreshAccounts(); -} - -void _addAccount(String label) => addAccountSync(label: label); - -void _setLabelForAccount(Map args) { - final label = args['label'] as String; - final accountIndex = args['accountIndex'] as int; - - setLabelForAccountSync(label: label, accountIndex: accountIndex); -} - -Future addAccount({required String label}) async { - _addAccount(label); +void addAccount({required String label}) { + currentWallet!.addSubaddressAccount(label: label); unawaited(store()); } -Future setLabelForAccount({required int accountIndex, required String label}) async { - _setLabelForAccount({'accountIndex': accountIndex, 'label': label}); - unawaited(store()); -} \ No newline at end of file +void setLabelForAccount({required int accountIndex, required String label}) { + subaddressAccount!.setLabel(accountIndex: accountIndex, label: label); + MoneroAccountListBase.cachedAccounts[currentWallet!.ffiAddress()] = []; + refreshAccounts(); + unawaited(store()); +} diff --git a/cw_monero/lib/api/coins_info.dart b/cw_monero/lib/api/coins_info.dart index 83382f001..b08dbb1f7 100644 --- a/cw_monero/lib/api/coins_info.dart +++ b/cw_monero/lib/api/coins_info.dart @@ -3,17 +3,18 @@ import 'dart:isolate'; import 'package:cw_monero/api/account_list.dart'; import 'package:monero/monero.dart' as monero; +import 'package:monero/src/wallet2.dart'; import 'package:mutex/mutex.dart'; -monero.Coins? coins = null; +Wallet2Coins? coins = null; final coinsMutex = Mutex(); Future refreshCoins(int accountIndex) async { if (coinsMutex.isLocked) { return; } - coins = monero.Wallet_coins(wptr!); - final coinsPtr = coins!.address; + coins = currentWallet!.coins(); + final coinsPtr = coins!.ffiAddress(); await coinsMutex.acquire(); await Isolate.run(() => monero.Coins_refresh(Pointer.fromAddress(coinsPtr))); coinsMutex.release(); @@ -21,14 +22,14 @@ Future refreshCoins(int accountIndex) async { Future countOfCoins() async { await coinsMutex.acquire(); - final count = monero.Coins_count(coins!); + final count = coins!.count(); coinsMutex.release(); return count; } -Future getCoin(int index) async { +Future getCoin(int index) async { await coinsMutex.acquire(); - final coin = monero.Coins_coin(coins!, index); + final coin = coins!.coin(index); coinsMutex.release(); return coin; } @@ -37,7 +38,7 @@ Future getCoinByKeyImage(String keyImage) async { final count = await countOfCoins(); for (int i = 0; i < count; i++) { final coin = await getCoin(i); - final coinAddress = monero.CoinsInfo_keyImage(coin); + final coinAddress = coin.keyImage; if (keyImage == coinAddress) { return i; } @@ -47,14 +48,14 @@ Future getCoinByKeyImage(String keyImage) async { Future freezeCoin(int index) async { await coinsMutex.acquire(); - final coinsPtr = coins!.address; + final coinsPtr = coins!.ffiAddress(); await Isolate.run(() => monero.Coins_setFrozen(Pointer.fromAddress(coinsPtr), index: index)); coinsMutex.release(); } Future thawCoin(int index) async { await coinsMutex.acquire(); - final coinsPtr = coins!.address; + final coinsPtr = coins!.ffiAddress(); await Isolate.run(() => monero.Coins_thaw(Pointer.fromAddress(coinsPtr), index: index)); coinsMutex.release(); } diff --git a/cw_monero/lib/api/subaddress_list.dart b/cw_monero/lib/api/subaddress_list.dart index 5db8a5d5f..46bdc10bc 100644 --- a/cw_monero/lib/api/subaddress_list.dart +++ b/cw_monero/lib/api/subaddress_list.dart @@ -2,7 +2,8 @@ import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/transaction_history.dart'; import 'package:cw_monero/api/wallet.dart'; -import 'package:monero/monero.dart' as monero; +import 'package:monero/monero.dart'; +import 'package:monero/src/monero.dart'; bool isUpdating = false; @@ -16,7 +17,7 @@ class SubaddressInfoMetadata { SubaddressInfoMetadata? subaddress = null; String getRawLabel({required int accountIndex, required int addressIndex}) { - return monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex); + return currentWallet!.getSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex); } void refreshSubaddresses({required int accountIndex}) { @@ -46,7 +47,7 @@ class Subaddress { final int received; final int txCount; String get label { - final localLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex); + final localLabel = currentWallet!.getSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex); if (localLabel.startsWith("#$addressIndex")) return localLabel; // don't duplicate the ID if it was user-providen return "#$addressIndex ${localLabel}".trim(); } @@ -66,26 +67,26 @@ int lastTxCount = 0; List ttDetails = []; List getAllSubaddresses() { - txhistory = monero.Wallet_history(wptr!); - final txCount = monero.TransactionHistory_count(txhistory!); - if (lastTxCount != txCount && lastWptr != wptr!.address) { + txhistory = currentWallet!.history(); + final txCount = txhistory!.count(); + if (lastTxCount != txCount && lastWptr != currentWallet!.ffiAddress()) { final List newttDetails = []; lastTxCount = txCount; - lastWptr = wptr!.address; + lastWptr = currentWallet!.ffiAddress(); for (var i = 0; i < txCount; i++) { - final tx = monero.TransactionHistory_transaction(txhistory!, index: i); - if (monero.TransactionInfo_direction(tx) == monero.TransactionInfo_Direction.Out) continue; - final subaddrs = monero.TransactionInfo_subaddrIndex(tx).split(","); - final account = monero.TransactionInfo_subaddrAccount(tx); + final tx = txhistory!.transaction(i); + if (tx.direction() == TransactionInfo_Direction.Out.index) continue; + final subaddrs = tx.subaddrIndex().split(","); + final account = tx.subaddrAccount(); newttDetails.add(TinyTransactionDetails( address: List.generate(subaddrs.length, (index) => getAddress(accountIndex: account, addressIndex: int.tryParse(subaddrs[index])??0)), - amount: monero.TransactionInfo_amount(tx), + amount: tx.amount(), )); } ttDetails.clear(); ttDetails.addAll(newttDetails); } - final size = monero.Wallet_numSubaddresses(wptr!, accountIndex: subaddress!.accountIndex); + final size = currentWallet!.numSubaddresses(accountIndex: subaddress!.accountIndex); final list = List.generate(size, (index) { final ttDetailsLocal = ttDetails.where((element) { final address = getAddress( @@ -119,46 +120,17 @@ List getAllSubaddresses() { } int numSubaddresses(int subaccountIndex) { - return monero.Wallet_numSubaddresses(wptr!, accountIndex: subaccountIndex); -} - -void addSubaddressSync({required int accountIndex, required String label}) { - monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex, label: label); - refreshSubaddresses(accountIndex: accountIndex); -} - -void setLabelForSubaddressSync( - {required int accountIndex, required int addressIndex, required String label}) { - monero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex, label: label); -} - -void _addSubaddress(Map args) { - final label = args['label'] as String; - final accountIndex = args['accountIndex'] as int; - - addSubaddressSync(accountIndex: accountIndex, label: label); -} - -void _setLabelForSubaddress(Map args) { - final label = args['label'] as String; - final accountIndex = args['accountIndex'] as int; - final addressIndex = args['addressIndex'] as int; - - setLabelForSubaddressSync( - accountIndex: accountIndex, addressIndex: addressIndex, label: label); + return currentWallet!.numSubaddresses(accountIndex: subaccountIndex); } Future addSubaddress({required int accountIndex, required String label}) async { - _addSubaddress({'accountIndex': accountIndex, 'label': label}); + currentWallet!.addSubaddress(accountIndex: accountIndex, label: label); + refreshSubaddresses(accountIndex: accountIndex); await store(); } Future setLabelForSubaddress( - {required int accountIndex, required int addressIndex, required String label}) async { - _setLabelForSubaddress({ - 'accountIndex': accountIndex, - 'addressIndex': addressIndex, - 'label': label - }); + {required int accountIndex, required int addressIndex, required String label}) async { + currentWallet!.setSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex, label: label); await store(); -} +} \ No newline at end of file diff --git a/cw_monero/lib/api/transaction_history.dart b/cw_monero/lib/api/transaction_history.dart index 8c7b1e902..4360149d2 100644 --- a/cw_monero/lib/api/transaction_history.dart +++ b/cw_monero/lib/api/transaction_history.dart @@ -9,36 +9,38 @@ import 'package:cw_monero/api/structs/pending_transaction.dart'; import 'package:cw_monero/api/wallet.dart'; import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart'; import 'package:ffi/ffi.dart'; +import 'package:monero/src/monero.dart'; import 'package:monero/monero.dart' as monero; +import 'package:monero/src/wallet2.dart'; import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen; import 'package:mutex/mutex.dart'; Map> txKeys = {}; String getTxKey(String txId) { - txKeys[wptr!.address] ??= {}; - if (txKeys[wptr!.address]![txId] != null) { - return txKeys[wptr!.address]![txId]!; + txKeys[currentWallet!.ffiAddress()] ??= {}; + if (txKeys[currentWallet!.ffiAddress()]![txId] != null) { + return txKeys[currentWallet!.ffiAddress()]![txId]!; } - final txKey = monero.Wallet_getTxKey(wptr!, txid: txId); - final status = monero.Wallet_status(wptr!); + final txKey = currentWallet!.getTxKey(txid: txId); + final status = currentWallet!.status(); if (status != 0) { - monero.Wallet_errorString(wptr!); - txKeys[wptr!.address]![txId] = ""; + currentWallet!.errorString(); + txKeys[currentWallet!.ffiAddress()]![txId] = ""; return ""; } - txKeys[wptr!.address]![txId] = txKey; + txKeys[currentWallet!.ffiAddress()]![txId] = txKey; return txKey; } final txHistoryMutex = Mutex(); -monero.TransactionHistory? txhistory; +Wallet2TransactionHistory? txhistory; bool isRefreshingTx = false; Future refreshTransactions() async { if (isRefreshingTx == true) return; isRefreshingTx = true; - txhistory ??= monero.Wallet_history(wptr!); - final ptr = txhistory!.address; + txhistory ??= currentWallet!.history(); + final ptr = txhistory!.ffiAddress(); await txHistoryMutex.acquire(); await Isolate.run(() { monero.TransactionHistory_refresh(Pointer.fromAddress(ptr)); @@ -48,14 +50,14 @@ Future refreshTransactions() async { isRefreshingTx = false; } -int countOfTransactions() => monero.TransactionHistory_count(txhistory!); +int countOfTransactions() => txhistory!.count(); Future> getAllTransactions() async { List dummyTxs = []; await txHistoryMutex.acquire(); - txhistory ??= monero.Wallet_history(wptr!); - final startAddress = txhistory!.address * wptr!.address; + txhistory ??= currentWallet!.history(); + final startAddress = txhistory!.ffiAddress() * currentWallet!.ffiAddress(); int size = countOfTransactions(); final list = []; for (int index = 0; index < size; index++) { @@ -63,21 +65,21 @@ Future> getAllTransactions() async { // Give main thread a chance to do other things. await Future.delayed(Duration.zero); } - if (txhistory!.address * wptr!.address != startAddress) { + if (txhistory!.ffiAddress() * currentWallet!.ffiAddress() != startAddress) { printV("Loop broken because txhistory!.address * wptr!.address != startAddress"); break; } - final txInfo = monero.TransactionHistory_transaction(txhistory!, index: index); - final txHash = monero.TransactionInfo_hash(txInfo); - txCache[wptr!.address] ??= {}; - txCache[wptr!.address]![txHash] = Transaction(txInfo: txInfo); - list.add(txCache[wptr!.address]![txHash]!); + final txInfo = txhistory!.transaction(index); + final txHash = txInfo.hash(); + txCache[currentWallet!.ffiAddress()] ??= {}; + txCache[currentWallet!.ffiAddress()]![txHash] = Transaction(txInfo: txInfo); + list.add(txCache[currentWallet!.ffiAddress()]![txHash]!); } txHistoryMutex.release(); - final accts = monero.Wallet_numSubaddressAccounts(wptr!); + final accts = currentWallet!.numSubaddressAccounts(); for (var i = 0; i < accts; i++) { - final fullBalance = monero.Wallet_balance(wptr!, accountIndex: i); - final availBalance = monero.Wallet_unlockedBalance(wptr!, accountIndex: i); + final fullBalance = currentWallet!.balance(accountIndex: i); + final availBalance = currentWallet!.unlockedBalance(accountIndex: i); if (fullBalance > availBalance) { if (list.where((element) => element.accountIndex == i && element.isConfirmed == false).isEmpty) { dummyTxs.add( @@ -95,7 +97,7 @@ Future> getAllTransactions() async { isSpend: false, hash: "pending", key: "", - txInfo: Pointer.fromAddress(0), + txInfo: DummyTransaction(), )..timeStamp = DateTime.now() ); } @@ -105,16 +107,21 @@ Future> getAllTransactions() async { return list; } +class DummyTransaction implements Wallet2TransactionInfo { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + Map> txCache = {}; Future getTransaction(String txId) async { - if (txCache[wptr!.address] != null && txCache[wptr!.address]![txId] != null) { - return txCache[wptr!.address]![txId]!; + if (txCache[currentWallet!.ffiAddress()] != null && txCache[currentWallet!.ffiAddress()]![txId] != null) { + return txCache[currentWallet!.ffiAddress()]![txId]!; } await txHistoryMutex.acquire(); - final tx = monero.TransactionHistory_transactionById(txhistory!, txid: txId); + final tx = txhistory!.transactionById(txId); final txDart = Transaction(txInfo: tx); - txCache[wptr!.address] ??= {}; - txCache[wptr!.address]![txId] = txDart; + txCache[currentWallet!.ffiAddress()] ??= {}; + txCache[currentWallet!.ffiAddress()]![txId] = txDart; txHistoryMutex.release(); return txDart; } @@ -127,9 +134,9 @@ Future createTransactionSync( int accountIndex = 0, List preferredInputs = const []}) async { - final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount); + final amt = amount == null ? 0 : currentWallet!.amountFromString(amount); - final waddr = wptr!.address; + final waddr = currentWallet!.ffiAddress(); // force reconnection in case the os killed the connection? // fixes failed to get block height error. @@ -149,7 +156,7 @@ Future createTransactionSync( final paymentIdAddr = paymentId_.address; final preferredInputsAddr = preferredInputs_.address; final spaddr = monero.defaultSeparator.address; - final pendingTx = Pointer.fromAddress(await Isolate.run(() { + final pendingTxPtr = Pointer.fromAddress(await Isolate.run(() { final tx = monero_gen.MoneroC(DynamicLibrary.open(monero.libPath)).MONERO_Wallet_createTransaction( Pointer.fromAddress(waddr), Pointer.fromAddress(addraddr).cast(), @@ -163,15 +170,16 @@ Future createTransactionSync( ); return tx.address; })); + final Wallet2PendingTransaction pendingTx = MoneroPendingTransaction(pendingTxPtr); calloc.free(address_); calloc.free(paymentId_); calloc.free(preferredInputs_); final String? error = (() { - final status = monero.PendingTransaction_status(pendingTx); + final status = pendingTx.status(); if (status == 0) { return null; } - return monero.PendingTransaction_errorString(pendingTx); + return pendingTx.errorString(); })(); if (error != null) { @@ -182,10 +190,10 @@ Future createTransactionSync( throw CreationTransactionException(message: message); } - final rAmt = monero.PendingTransaction_amount(pendingTx); - final rFee = monero.PendingTransaction_fee(pendingTx); - final rHash = monero.PendingTransaction_txid(pendingTx, ''); - final rHex = monero.PendingTransaction_hex(pendingTx, ''); + final rAmt = pendingTx.amount(); + final rFee = pendingTx.fee(); + final rHash = pendingTx.txid(''); + final rHex = pendingTx.hex(''); final rTxKey = rHash; return PendingTransactionDescription( @@ -194,7 +202,7 @@ Future createTransactionSync( hash: rHash, hex: rHex, txKey: rTxKey, - pointerAddress: pendingTx.address, + pointerAddress: pendingTx.ffiAddress(), ); } @@ -206,9 +214,9 @@ Future createTransactionMultDest( List preferredInputs = const []}) async { final dstAddrs = outputs.map((e) => e.address).toList(); - final amounts = outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList(); + final amounts = outputs.map((e) => currentWallet!.amountFromString(e.amount)).toList(); - final waddr = wptr!.address; + final waddr = currentWallet!.ffiAddress(); // force reconnection in case the os killed the connection Isolate.run(() async { @@ -227,49 +235,50 @@ Future createTransactionMultDest( ).address; })); - if (monero.PendingTransaction_status(txptr) != 0) { - throw CreationTransactionException(message: monero.PendingTransaction_errorString(txptr)); + final Wallet2PendingTransaction tx = MoneroPendingTransaction(txptr); + + if (tx.status() != 0) { + throw CreationTransactionException(message: tx.errorString()); } return PendingTransactionDescription( - amount: monero.PendingTransaction_amount(txptr), - fee: monero.PendingTransaction_fee(txptr), - hash: monero.PendingTransaction_txid(txptr, ''), - hex: monero.PendingTransaction_hex(txptr, ''), - txKey: monero.PendingTransaction_txid(txptr, ''), - pointerAddress: txptr.address, + amount: tx.amount(), + fee: tx.fee(), + hash: tx.txid(''), + hex: tx.hex(''), + txKey: tx.txid(''), + pointerAddress: tx.ffiAddress(), ); } String? commitTransactionFromPointerAddress({required int address, required bool useUR}) => - commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address), useUR: useUR); + commitTransaction(tx: MoneroPendingTransaction(Pointer.fromAddress(address)), useUR: useUR); -String? commitTransaction({required monero.PendingTransaction transactionPointer, required bool useUR}) { - final transactionPointerAddress = transactionPointer.address; +String? commitTransaction({required Wallet2PendingTransaction tx, required bool useUR}) { final txCommit = useUR - ? monero.PendingTransaction_commitUR(transactionPointer, 120) + ? tx.commitUR(120) : Isolate.run(() { monero.PendingTransaction_commit( - Pointer.fromAddress(transactionPointerAddress), + Pointer.fromAddress(tx.ffiAddress()), filename: '', overwrite: false, ); }); String? error = (() { - final status = monero.PendingTransaction_status(transactionPointer.cast()); + final status = tx.status(); if (status == 0) { return null; } - return monero.PendingTransaction_errorString(transactionPointer.cast()); + return tx.errorString(); })(); if (error == null) { error = (() { - final status = monero.Wallet_status(wptr!); + final status = currentWallet!.status(); if (status == 0) { return null; } - return monero.Wallet_errorString(wptr!); + return currentWallet!.errorString(); })(); } @@ -283,43 +292,9 @@ String? commitTransaction({required monero.PendingTransaction transactionPointer } } -Future _createTransactionSync(Map args) async { - final address = args['address'] as String; - final paymentId = args['paymentId'] as String; - final amount = args['amount'] as String?; - final priorityRaw = args['priorityRaw'] as int; - final accountIndex = args['accountIndex'] as int; - final preferredInputs = args['preferredInputs'] as List; - - return createTransactionSync( - address: address, - paymentId: paymentId, - amount: amount, - priorityRaw: priorityRaw, - accountIndex: accountIndex, - preferredInputs: preferredInputs); -} - -Future createTransaction( - {required String address, - required int priorityRaw, - String? amount, - String paymentId = '', - int accountIndex = 0, - List preferredInputs = const []}) async => - _createTransactionSync({ - 'address': address, - 'paymentId': paymentId, - 'amount': amount, - 'priorityRaw': priorityRaw, - 'accountIndex': accountIndex, - 'preferredInputs': preferredInputs - }); - class Transaction { final String displayLabel; - late final String subaddressLabel = monero.Wallet_getSubaddressLabel( - wptr!, + late final String subaddressLabel = currentWallet!.getSubaddressLabel( accountIndex: accountIndex, addressIndex: addressIndex, ); @@ -372,26 +347,26 @@ class Transaction { // final SubAddress? subAddress; // List transfers = []; // final int txIndex; - final monero.TransactionInfo txInfo; + final Wallet2TransactionInfo txInfo; Transaction({ required this.txInfo, - }) : displayLabel = monero.TransactionInfo_label(txInfo), - hash = monero.TransactionInfo_hash(txInfo), + }) : displayLabel = txInfo.label(), + hash = txInfo.hash(), timeStamp = DateTime.fromMillisecondsSinceEpoch( - monero.TransactionInfo_timestamp(txInfo) * 1000, + txInfo.timestamp() * 1000, ), - isSpend = monero.TransactionInfo_direction(txInfo) == - monero.TransactionInfo_Direction.Out, - amount = monero.TransactionInfo_amount(txInfo), - paymentId = monero.TransactionInfo_paymentId(txInfo), - accountIndex = monero.TransactionInfo_subaddrAccount(txInfo), - addressIndex = int.tryParse(monero.TransactionInfo_subaddrIndex(txInfo).split(", ")[0]) ?? 0, - addressIndexList = monero.TransactionInfo_subaddrIndex(txInfo).split(", ").map((e) => int.tryParse(e) ?? 0).toList(), - blockheight = monero.TransactionInfo_blockHeight(txInfo), - confirmations = monero.TransactionInfo_confirmations(txInfo), - fee = monero.TransactionInfo_fee(txInfo), - description = monero.TransactionInfo_description(txInfo), - key = getTxKey(monero.TransactionInfo_hash(txInfo)); + isSpend = txInfo.direction() == + monero.TransactionInfo_Direction.Out.index, + amount = txInfo.amount(), + paymentId = txInfo.paymentId(), + accountIndex = txInfo.subaddrAccount(), + addressIndex = int.tryParse(txInfo.subaddrIndex().split(", ")[0]) ?? 0, + addressIndexList = txInfo.subaddrIndex().split(", ").map((e) => int.tryParse(e) ?? 0).toList(), + blockheight = txInfo.blockHeight(), + confirmations = txInfo.confirmations(), + fee = txInfo.fee(), + description = txInfo.description(), + key = getTxKey(txInfo.hash()); Transaction.dummy({ required this.displayLabel, diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index 7e64c7f08..87abdd95b 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -5,8 +5,6 @@ import 'dart:isolate'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart'; -import 'package:cw_monero/api/wallet_manager.dart'; -import 'package:flutter/foundation.dart'; import 'package:monero/monero.dart' as monero; import 'package:mutex/mutex.dart'; import 'package:polyseed/polyseed.dart'; @@ -15,36 +13,37 @@ bool debugMonero = false; int getSyncingHeight() { // final height = monero.MONERO_cw_WalletListener_height(getWlptr()); - final h2 = monero.Wallet_blockChainHeight(wptr!); + if (currentWallet == null) return 0; + final h2 = currentWallet!.blockChainHeight(); // printV("height: $height / $h2"); return h2; } bool isNeededToRefresh() { - final wlptr = getWlptr(); - if (wlptr == null) return false; - final ret = monero.MONERO_cw_WalletListener_isNeedToRefresh(wlptr); - monero.MONERO_cw_WalletListener_resetNeedToRefresh(wlptr); + final wl = getWlptr(); + if (wl == null) return false; + final ret = wl.isNeedToRefresh(); + wl.resetNeedToRefresh(); return ret; } bool isNewTransactionExist() { final wlptr = getWlptr(); if (wlptr == null) return false; - final ret = monero.MONERO_cw_WalletListener_isNewTransactionExist(wlptr); - monero.MONERO_cw_WalletListener_resetIsNewTransactionExist(wlptr); + final ret = wlptr.isNewTransactionExist(); + wlptr.resetIsNewTransactionExist(); return ret; } -String getFilename() => monero.Wallet_filename(wptr!); +String getFilename() => currentWallet!.filename(); String getSeed() { // monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); final cakepolyseed = - monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed"); + currentWallet!.getCacheAttribute(key: "cakewallet.seed"); final cakepassphrase = getPassphrase(); - final weirdPolyseed = monero.Wallet_getPolyseed(wptr!, passphrase: cakepassphrase); + final weirdPolyseed = currentWallet!.getPolyseed(passphrase: cakepassphrase); if (weirdPolyseed != "") return weirdPolyseed; if (cakepolyseed != "") { @@ -63,7 +62,7 @@ String getSeed() { return cakepolyseed; } - final bip39 = monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed.bip39"); + final bip39 = currentWallet!.getCacheAttribute(key: "cakewallet.seed.bip39"); if(bip39.isNotEmpty) return bip39; @@ -85,29 +84,29 @@ String? getSeedLanguage(String? language) { String getSeedLegacy(String? language) { final cakepassphrase = getPassphrase(); language = getSeedLanguage(language); - var legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase); - if (monero.Wallet_status(wptr!) != 0) { - if (monero.Wallet_errorString(wptr!).contains("seed_language")) { - monero.Wallet_setSeedLanguage(wptr!, language: "English"); - legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase); + var legacy = currentWallet!.seed(seedOffset: cakepassphrase); + if (currentWallet!.status() != 0) { + if (currentWallet!.errorString().contains("seed_language")) { + currentWallet!.setSeedLanguage(language: "English"); + legacy = currentWallet!.seed(seedOffset: cakepassphrase); } } if (language != null) { - monero.Wallet_setSeedLanguage(wptr!, language: language); - final status = monero.Wallet_status(wptr!); + currentWallet!.setSeedLanguage(language: language); + final status = currentWallet!.status(); if (status != 0) { - final err = monero.Wallet_errorString(wptr!); + final err = currentWallet!.errorString(); if (legacy.isNotEmpty) { return "$err\n\n$legacy"; } return err; } - legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase); + legacy = currentWallet!.seed(seedOffset: cakepassphrase); } - if (monero.Wallet_status(wptr!) != 0) { - final err = monero.Wallet_errorString(wptr!); + if (currentWallet!.status() != 0) { + final err = currentWallet!.errorString(); if (legacy.isNotEmpty) { return "$err\n\n$legacy"; } @@ -117,7 +116,7 @@ String getSeedLegacy(String? language) { } String getPassphrase() { - return monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.passphrase"); + return currentWallet!.getCacheAttribute(key: "cakewallet.passphrase"); } Map>> addressCache = {}; @@ -125,31 +124,31 @@ Map>> addressCache = {}; String getAddress({int accountIndex = 0, int addressIndex = 0}) { // printV("getaddress: ${accountIndex}/${addressIndex}: ${monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)}: ${monero.Wallet_address(wptr!, accountIndex: accountIndex, addressIndex: addressIndex)}"); // this could be a while loop, but I'm in favor of making it if to not cause freezes - if (monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)-1 < addressIndex) { - if (monero.Wallet_numSubaddressAccounts(wptr!) < accountIndex) { - monero.Wallet_addSubaddressAccount(wptr!); + if (currentWallet!.numSubaddresses(accountIndex: accountIndex)-1 < addressIndex) { + if (currentWallet!.numSubaddressAccounts() < accountIndex) { + currentWallet!.addSubaddressAccount(); } else { - monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex); + currentWallet!.addSubaddress(accountIndex: accountIndex); } } - addressCache[wptr!.address] ??= {}; - addressCache[wptr!.address]![accountIndex] ??= {}; - addressCache[wptr!.address]![accountIndex]![addressIndex] ??= monero.Wallet_address(wptr!, + addressCache[currentWallet!.ffiAddress()] ??= {}; + addressCache[currentWallet!.ffiAddress()]![accountIndex] ??= {}; + addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex] ??= currentWallet!.address( accountIndex: accountIndex, addressIndex: addressIndex); - return addressCache[wptr!.address]![accountIndex]![addressIndex]!; + return addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex]!; } int getFullBalance({int accountIndex = 0}) => - monero.Wallet_balance(wptr!, accountIndex: accountIndex); + currentWallet!.balance(accountIndex: accountIndex); int getUnlockedBalance({int accountIndex = 0}) => - monero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex); + currentWallet!.unlockedBalance(accountIndex: accountIndex); -int getCurrentHeight() => monero.Wallet_blockChainHeight(wptr!); +int getCurrentHeight() => currentWallet!.blockChainHeight(); -int getNodeHeightSync() => monero.Wallet_daemonBlockChainHeight(wptr!); +int getNodeHeightSync() => currentWallet!.daemonBlockChainHeight(); -bool isConnectedSync() => monero.Wallet_connected(wptr!) != 0; +bool isConnectedSync() => currentWallet!.connected() != 0; Future setupNodeSync( {required String address, @@ -168,7 +167,7 @@ Future setupNodeSync( daemonPassword: $password ?? '' } '''); - final addr = wptr!.address; + final addr = currentWallet!.ffiAddress(); printV("init: start"); await Isolate.run(() { monero.Wallet_init(Pointer.fromAddress(addr), @@ -180,10 +179,10 @@ Future setupNodeSync( }); printV("init: end"); - final status = monero.Wallet_status(wptr!); + final status = currentWallet!.status(); if (status != 0) { - final error = monero.Wallet_errorString(wptr!); + final error = currentWallet!.errorString(); if (error != "no tx keys found for this txid") { printV("error: $error"); throw SetupWalletException(message: error); @@ -191,8 +190,8 @@ Future setupNodeSync( } if (true) { - monero.Wallet_init3( - wptr!, argv0: '', + currentWallet!.init3( + argv0: '', defaultLogBaseName: 'moneroc', console: true, logPath: '', @@ -203,19 +202,19 @@ Future setupNodeSync( } void startRefreshSync() { - monero.Wallet_refreshAsync(wptr!); - monero.Wallet_startRefresh(wptr!); + currentWallet!.refreshAsync(); + currentWallet!.startRefresh(); } void setRefreshFromBlockHeight({required int height}) { - monero.Wallet_setRefreshFromBlockHeight(wptr!, + currentWallet!.setRefreshFromBlockHeight( refresh_from_block_height: height); } void setRecoveringFromSeed({required bool isRecovery}) { - monero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery); - monero.Wallet_store(wptr!); + currentWallet!.setRecoveringFromSeed(recoveringFromSeed: isRecovery); + currentWallet!.store(); } final storeMutex = Mutex(); @@ -224,18 +223,18 @@ final storeMutex = Mutex(); int lastStorePointer = 0; int lastStoreHeight = 0; void storeSync({bool force = false}) async { - final addr = wptr!.address; + final addr = currentWallet!.ffiAddress(); final synchronized = await Isolate.run(() { return monero.Wallet_synchronized(Pointer.fromAddress(addr)); }); - if (lastStorePointer == wptr!.address && - lastStoreHeight + 5000 > monero.Wallet_blockChainHeight(wptr!) && + if (lastStorePointer == addr && + lastStoreHeight + 5000 > currentWallet!.blockChainHeight() && !synchronized && !force) { return; } - lastStorePointer = wptr!.address; - lastStoreHeight = monero.Wallet_blockChainHeight(wptr!); + lastStorePointer = currentWallet!.ffiAddress(); + lastStoreHeight = currentWallet!.blockChainHeight(); await storeMutex.acquire(); await Isolate.run(() { monero.Wallet_store(Pointer.fromAddress(addr)); @@ -244,25 +243,25 @@ void storeSync({bool force = false}) async { } void setPasswordSync(String password) { - monero.Wallet_setPassword(wptr!, password: password); + currentWallet!.setPassword(password: password); - final status = monero.Wallet_status(wptr!); + final status = currentWallet!.status(); if (status != 0) { - throw Exception(monero.Wallet_errorString(wptr!)); + throw Exception(currentWallet!.errorString()); } } void closeCurrentWallet() { - monero.Wallet_stop(wptr!); + currentWallet!.stop(); } -String getSecretViewKey() => monero.Wallet_secretViewKey(wptr!); +String getSecretViewKey() => currentWallet!.secretViewKey(); -String getPublicViewKey() => monero.Wallet_publicViewKey(wptr!); +String getPublicViewKey() => currentWallet!.publicViewKey(); -String getSecretSpendKey() => monero.Wallet_secretSpendKey(wptr!); +String getSecretSpendKey() => currentWallet!.secretSpendKey(); -String getPublicSpendKey() => monero.Wallet_publicSpendKey(wptr!); +String getPublicSpendKey() => currentWallet!.publicSpendKey(); class SyncListener { SyncListener(this.onNewBlock, this.onNewTransaction) @@ -360,52 +359,32 @@ Future _setupNodeSync(Map args) async { socksProxyAddress: socksProxyAddress); } -bool _isConnected(Object _) => isConnectedSync(); - -int _getNodeHeight(Object _) => getNodeHeightSync(); - void startRefresh() => startRefreshSync(); -Future setupNode( - {required String address, - String? login, - String? password, - bool useSSL = false, - String? socksProxyAddress, - bool isLightWallet = false}) async => - _setupNodeSync({ - 'address': address, - 'login': login, - 'password': password, - 'useSSL': useSSL, - 'isLightWallet': isLightWallet, - 'socksProxyAddress': socksProxyAddress - }); - Future store() async => _storeSync(0); -Future isConnected() async => _isConnected(0); +Future isConnected() async => isConnectedSync(); -Future getNodeHeight() async => _getNodeHeight(0); +Future getNodeHeight() async => getNodeHeightSync(); -void rescanBlockchainAsync() => monero.Wallet_rescanBlockchainAsync(wptr!); +void rescanBlockchainAsync() => currentWallet!.rescanBlockchainAsync(); String getSubaddressLabel(int accountIndex, int addressIndex) { - return monero.Wallet_getSubaddressLabel(wptr!, + return currentWallet!.getSubaddressLabel( accountIndex: accountIndex, addressIndex: addressIndex); } Future setTrustedDaemon(bool trusted) async => - monero.Wallet_setTrustedDaemon(wptr!, arg: trusted); + currentWallet!.setTrustedDaemon(arg: trusted); -Future trustedDaemon() async => monero.Wallet_trustedDaemon(wptr!); +Future trustedDaemon() async => currentWallet!.trustedDaemon(); String signMessage(String message, {String address = ""}) { - return monero.Wallet_signMessage(wptr!, message: message, address: address); + return currentWallet!.signMessage(message: message, address: address); } bool verifyMessage(String message, String address, String signature) { - return monero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature); + return currentWallet!.verifySignedMessage(message: message, address: address, signature: signature); } Map> debugCallLength() => monero.debugCallLength; diff --git a/cw_monero/lib/api/wallet_manager.dart b/cw_monero/lib/api/wallet_manager.dart index b43773447..9b369c155 100644 --- a/cw_monero/lib/api/wallet_manager.dart +++ b/cw_monero/lib/api/wallet_manager.dart @@ -12,6 +12,8 @@ import 'package:cw_monero/api/transaction_history.dart'; import 'package:cw_monero/api/wallet.dart'; import 'package:cw_monero/ledger.dart'; import 'package:flutter/foundation.dart'; +import 'package:monero/src/monero.dart'; +import 'package:monero/src/wallet2.dart'; import 'package:monero/monero.dart' as monero; class MoneroCException implements Exception { @@ -24,9 +26,10 @@ class MoneroCException implements Exception { } void checkIfMoneroCIsFine() { - final cppCsCpp = monero.MONERO_checksum_wallet2_api_c_cpp(); - final cppCsH = monero.MONERO_checksum_wallet2_api_c_h(); - final cppCsExp = monero.MONERO_checksum_wallet2_api_c_exp(); + final checksum = MoneroWalletChecksum(); + final cppCsCpp = checksum.checksum_wallet2_api_c_cpp(); + final cppCsH = checksum.checksum_wallet2_api_c_h(); + final cppCsExp = checksum.checksum_wallet2_api_c_exp(); final dartCsCpp = monero.wallet2_api_c_cpp_sha256; final dartCsH = monero.wallet2_api_c_h_sha256; @@ -44,36 +47,35 @@ void checkIfMoneroCIsFine() { throw MoneroCException("monero_c and monero.dart wrapper export list mismatch.\nLogic errors can occur.\nRefusing to run in release mode.\ncpp: '$cppCsExp'\ndart: '$dartCsExp'"); } } -monero.WalletManager? _wmPtr; -final monero.WalletManager wmPtr = Pointer.fromAddress((() { +Wallet2WalletManager? _wmPtr; +Wallet2WalletManager wmPtr = (() { try { // Problems with the wallet? Crashes? Lags? this will print all calls to xmr // codebase, so it will be easier to debug what happens. At least easier // than plugging gdb in. Especially on windows/android. monero.printStarts = false; if (kDebugMode && debugMonero) { - monero.WalletManagerFactory_setLogLevel(4); + MoneroWalletManagerFactory().setLogLevel(4); } - _wmPtr ??= monero.WalletManagerFactory_getWalletManager(); + _wmPtr ??= MoneroWalletManagerFactory().getWalletManager(); if (kDebugMode && debugMonero) { - monero.WalletManagerFactory_setLogLevel(4); + MoneroWalletManagerFactory().setLogLevel(4); } printV("ptr: $_wmPtr"); } catch (e) { printV(e); rethrow; } - return _wmPtr!.address; -})()); + return _wmPtr!; +})(); -void createWalletPointer() { - final newWptr = monero.WalletManager_createWallet(wmPtr, +Wallet2Wallet createWalletPointer() { + final newWptr = wmPtr.createWallet( path: "", password: "", language: "", networkType: 0); - - wptr = newWptr; + return newWptr; } -void createWalletSync( +void createWallet( {required String path, required String password, required String language, @@ -81,28 +83,25 @@ void createWalletSync( int nettype = 0}) { txhistory = null; language = getSeedLanguage(language)!; - final newWptr = monero.WalletManager_createWallet(wmPtr, + final newW = wmPtr.createWallet( path: path, password: password, language: language, networkType: 0); - int status = monero.Wallet_status(newWptr); + int status = newW.status(); if (status != 0) { - throw WalletCreationException(message: monero.Wallet_errorString(newWptr)); + throw WalletCreationException(message: newW.errorString()); } - setupBackgroundSync(password, newWptr); + setupBackgroundSync(password, newW); - wptr = newWptr; - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase); - monero.Wallet_store(wptr!, path: path); - openedWalletsByPath[path] = wptr!; + currentWallet = newW; + currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase); + currentWallet!.store(path: path); + openedWalletsByPath[path] = currentWallet!; _lastOpenedWallet = path; - - // is the line below needed? - // setupNodeSync(address: "node.moneroworld.com:18089"); } -bool isWalletExistSync({required String path}) { - return monero.WalletManager_walletExists(wmPtr, path); +bool isWalletExist({required String path}) { + return wmPtr.walletExists(path); } void restoreWalletFromSeedSync( @@ -113,8 +112,7 @@ void restoreWalletFromSeedSync( int nettype = 0, int restoreHeight = 0}) { txhistory = null; - final newWptr = monero.WalletManager_recoveryWallet( - wmPtr, + final newW = wmPtr.recoveryWallet( path: path, password: password, mnemonic: seed, @@ -123,10 +121,10 @@ void restoreWalletFromSeedSync( networkType: 0, ); - final status = monero.Wallet_status(newWptr); + final status = newW.status(); if (status != 0) { - final error = monero.Wallet_errorString(newWptr); + final error = newW.errorString(); if (error.contains('word list failed verification')) { throw WalletRestoreFromSeedException( message: "Seed verification failed, please make sure you entered the correct seed with the correct words order", @@ -134,20 +132,20 @@ void restoreWalletFromSeedSync( } throw WalletRestoreFromSeedException(message: error); } - wptr = newWptr; + currentWallet = newW; setRefreshFromBlockHeight(height: restoreHeight); - setupBackgroundSync(password, newWptr); + setupBackgroundSync(password, newW); - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase); + currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase); - openedWalletsByPath[path] = wptr!; + openedWalletsByPath[path] = currentWallet!; - monero.Wallet_store(wptr!); + currentWallet!.store(path: path); _lastOpenedWallet = path; } -void restoreWalletFromKeysSync( +void restoreWalletFromKeys( {required String path, required String password, required String language, @@ -157,8 +155,8 @@ void restoreWalletFromKeysSync( int nettype = 0, int restoreHeight = 0}) { txhistory = null; - var newWptr = (spendKey != "") - ? monero.WalletManager_createDeterministicWalletFromSpendKey(wmPtr, + var newW = (spendKey != "") + ? wmPtr.createDeterministicWalletFromSpendKey( path: path, password: password, language: language, @@ -166,8 +164,7 @@ void restoreWalletFromKeysSync( newWallet: true, // TODO(mrcyjanek): safe to remove restoreHeight: restoreHeight) - : monero.WalletManager_createWalletFromKeys( - wmPtr, + : wmPtr.createWalletFromKeys( path: path, password: password, restoreHeight: restoreHeight, @@ -177,22 +174,21 @@ void restoreWalletFromKeysSync( nettype: 0, ); - int status = monero.Wallet_status(newWptr); + int status = newW.status(); if (status != 0) { throw WalletRestoreFromKeysException( - message: monero.Wallet_errorString(newWptr)); + message: newW.errorString()); } // CW-712 - Try to restore deterministic wallet first, if the view key doesn't // match the view key provided if (spendKey != "") { - final viewKeyRestored = monero.Wallet_secretViewKey(newWptr); + final viewKeyRestored = newW.secretViewKey(); if (viewKey != viewKeyRestored && viewKey != "") { - monero.WalletManager_closeWallet(wmPtr, newWptr, false); + wmPtr.closeWallet(newW, false); File(path).deleteSync(); File(path + ".keys").deleteSync(); - newWptr = monero.WalletManager_createWalletFromKeys( - wmPtr, + newW = wmPtr.createWalletFromKeys( path: path, password: password, restoreHeight: restoreHeight, @@ -201,19 +197,19 @@ void restoreWalletFromKeysSync( spendKeyString: spendKey, nettype: 0, ); - int status = monero.Wallet_status(newWptr); + int status = newW.status(); if (status != 0) { throw WalletRestoreFromKeysException( - message: monero.Wallet_errorString(newWptr)); + message: newW.errorString()); } - setupBackgroundSync(password, newWptr); + setupBackgroundSync(password, newW); } } - wptr = newWptr; + currentWallet = newW; - openedWalletsByPath[path] = wptr!; + openedWalletsByPath[path] = currentWallet!; _lastOpenedWallet = path; } @@ -228,8 +224,7 @@ void restoreWalletFromPolyseedWithOffset( int nettype = 0}) { txhistory = null; - final newWptr = monero.WalletManager_createWalletFromPolyseed( - wmPtr, + final newW = wmPtr.createWalletFromPolyseed( path: path, password: password, networkType: nettype, @@ -240,24 +235,24 @@ void restoreWalletFromPolyseedWithOffset( kdfRounds: 1, ); - int status = monero.Wallet_status(newWptr); + int status = newW.status(); if (status != 0) { - final err = monero.Wallet_errorString(newWptr); + final err = newW.errorString(); printV("err: $err"); throw WalletRestoreFromKeysException(message: err); } - wptr = newWptr; + currentWallet = newW; - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: seedOffset); - monero.Wallet_store(wptr!); + currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed); + currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: seedOffset); + currentWallet!.store(path: path); - setupBackgroundSync(password, newWptr); + setupBackgroundSync(password, currentWallet!); storeSync(); - openedWalletsByPath[path] = wptr!; + openedWalletsByPath[path] = currentWallet!; } @@ -282,8 +277,7 @@ void restoreWalletFromSpendKeySync( // ); txhistory = null; - final newWptr = monero.WalletManager_createDeterministicWalletFromSpendKey( - wmPtr, + final newW = wmPtr.createDeterministicWalletFromSpendKey( path: path, password: password, language: language, @@ -292,23 +286,23 @@ void restoreWalletFromSpendKeySync( restoreHeight: restoreHeight, ); - int status = monero.Wallet_status(newWptr); + int status = newW.status(); if (status != 0) { - final err = monero.Wallet_errorString(newWptr); + final err = newW.errorString(); printV("err: $err"); throw WalletRestoreFromKeysException(message: err); } - wptr = newWptr; + currentWallet = newW; - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); + currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed); storeSync(); - setupBackgroundSync(password, newWptr); + setupBackgroundSync(password, currentWallet!); - openedWalletsByPath[path] = wptr!; + openedWalletsByPath[path] = currentWallet!; _lastOpenedWallet = path; } @@ -321,41 +315,42 @@ Future restoreWalletFromHardwareWallet( int nettype = 0, int restoreHeight = 0}) async { txhistory = null; - + final wmPtr = MoneroWalletManagerFactory().getWalletManager().ffiAddress(); final newWptrAddr = await Isolate.run(() { - return monero.WalletManager_createWalletFromDevice(wmPtr, + return monero.WalletManager_createWalletFromDevice(Pointer.fromAddress(wmPtr), path: path, password: password, restoreHeight: restoreHeight, deviceName: deviceName) .address; }); - final newWptr = Pointer.fromAddress(newWptrAddr); + final newW = MoneroWallet(Pointer.fromAddress(newWptrAddr)); - final status = monero.Wallet_status(newWptr); + final status = newW.status(); if (status != 0) { - final error = monero.Wallet_errorString(newWptr); + final error = newW.errorString(); throw WalletRestoreFromSeedException(message: error); } - wptr = newWptr; + currentWallet = newW; + currentWallet!.store(path: path); _lastOpenedWallet = path; - openedWalletsByPath[path] = wptr!; + openedWalletsByPath[path] = currentWallet!; } -Map openedWalletsByPath = {}; +Map openedWalletsByPath = {}; Future loadWallet( {required String path, required String password, int nettype = 0}) async { if (openedWalletsByPath[path] != null) { txhistory = null; - wptr = openedWalletsByPath[path]!; + currentWallet = openedWalletsByPath[path]!; return; } - if (wptr == null || path != _lastOpenedWallet) { - if (wptr != null) { - final addr = wptr!.address; + if (currentWallet == null || path != _lastOpenedWallet) { + if (currentWallet != null) { + final addr = currentWallet!.ffiAddress(); Isolate.run(() { monero.Wallet_store(Pointer.fromAddress(addr)); }); @@ -366,19 +361,24 @@ Future loadWallet( /// 0: Software Wallet /// 1: Ledger /// 2: Trezor - late final deviceType; + var deviceType = 0; if (Platform.isAndroid || Platform.isIOS) { - deviceType = monero.WalletManager_queryWalletDevice( - wmPtr, + deviceType = wmPtr.queryWalletDevice( keysFileName: "$path.keys", password: password, kdfRounds: 1, ); - final status = monero.WalletManager_errorString(wmPtr); + final status = wmPtr.errorString(); if (status != "") { printV("loadWallet:"+status); - throw WalletOpeningException(message: status); + // This is most likely closeWallet call leaking error. This is fine. + if (status.contains("failed to save file")) { + printV("loadWallet: error leaked: $status"); + deviceType = 0; + } else { + throw WalletOpeningException(message: status); + } } } else { deviceType = 0; @@ -388,107 +388,47 @@ Future loadWallet( if (gLedger == null) { throw Exception("Tried to open a ledger wallet with no ledger connected"); } - final dummyWPtr = wptr ?? - monero.WalletManager_openWallet(wmPtr, path: '', password: ''); + final dummyWPtr = (currentWallet ?? + wmPtr.openWallet(path: '', password: '')); enableLedgerExchange(dummyWPtr, gLedger!); } - final addr = wmPtr.address; + final addr = wmPtr.ffiAddress(); final newWptrAddr = await Isolate.run(() { return monero.WalletManager_openWallet(Pointer.fromAddress(addr), path: path, password: password) .address; }); - final newWptr = Pointer.fromAddress(newWptrAddr); + final newW = MoneroWallet(Pointer.fromAddress(newWptrAddr)); - int status = monero.Wallet_status(newWptr); + int status = newW.status(); if (status != 0) { - final err = monero.Wallet_errorString(newWptr); + final err = newW.errorString(); printV("loadWallet:"+err); throw WalletOpeningException(message: err); } if (deviceType == 0) { - setupBackgroundSync(password, newWptr); + setupBackgroundSync(password, newW); } - wptr = newWptr; + currentWallet = newW; _lastOpenedWallet = path; - openedWalletsByPath[path] = wptr!; + openedWalletsByPath[path] = currentWallet!; } } -void setupBackgroundSync(String password, Pointer? wptrOverride) { - if (isViewOnlyBySpendKey(wptrOverride)) { +void setupBackgroundSync(String password, Wallet2Wallet wallet) { + if (isViewOnlyBySpendKey(wallet)) { return; } - monero.Wallet_setupBackgroundSync(wptrOverride ?? wptr!, backgroundSyncType: 2, walletPassword: password, backgroundCachePassword: ''); - if (monero.Wallet_status(wptrOverride ?? wptr!) != 0) { + wallet.setupBackgroundSync(backgroundSyncType: 2, walletPassword: password, backgroundCachePassword: ''); + if (wallet.status() != 0) { // We simply ignore the error. - printV("setupBackgroundSync: ${monero.Wallet_errorString(wptrOverride ?? wptr!)}"); + printV("setupBackgroundSync: ${wallet.errorString()}"); } } -void _createWallet(Map args) { - final path = args['path'] as String; - final password = args['password'] as String; - final language = args['language'] as String; - final passphrase = args['passphrase'] as String; - - createWalletSync(path: path, password: password, language: language, passphrase: passphrase); -} - -void _restoreFromSeed(Map args) { - final path = args['path'] as String; - final password = args['password'] as String; - final passphrase = args['passphrase'] as String; - final seed = args['seed'] as String; - final restoreHeight = args['restoreHeight'] as int; - - return restoreWalletFromSeedSync( - path: path, password: password, passphrase: passphrase, seed: seed, restoreHeight: restoreHeight); -} - -void _restoreFromKeys(Map args) { - final path = args['path'] as String; - final password = args['password'] as String; - final language = args['language'] as String; - final restoreHeight = args['restoreHeight'] as int; - final address = args['address'] as String; - final viewKey = args['viewKey'] as String; - final spendKey = args['spendKey'] as String; - - restoreWalletFromKeysSync( - path: path, - password: password, - language: language, - restoreHeight: restoreHeight, - address: address, - viewKey: viewKey, - spendKey: spendKey); -} - -void _restoreFromSpendKey(Map args) { - final path = args['path'] as String; - final password = args['password'] as String; - final seed = args['seed'] as String; - final language = args['language'] as String; - final spendKey = args['spendKey'] as String; - final restoreHeight = args['restoreHeight'] as int; - - restoreWalletFromSpendKeySync( - path: path, - password: password, - seed: seed, - language: language, - restoreHeight: restoreHeight, - spendKey: spendKey); -} - -Future _openWallet(Map args) async => loadWallet( - path: args['path'] as String, password: args['password'] as String); - -bool _isWalletExist(String path) => isWalletExistSync(path: path); Future openWallet( {required String path, @@ -496,77 +436,4 @@ Future openWallet( int nettype = 0}) async => loadWallet(path: path, password: password, nettype: nettype); -Future openWalletAsync(Map args) async => - _openWallet(args); - -Future createWallet( - {required String path, - required String password, - required String language, - required String passphrase, - int nettype = 0}) async => - _createWallet({ - 'path': path, - 'password': password, - 'language': language, - 'passphrase': passphrase, - 'nettype': nettype - }); - -void restoreFromSeed( - {required String path, - required String password, - required String passphrase, - required String seed, - int nettype = 0, - int restoreHeight = 0}) => - _restoreFromSeed({ - 'path': path, - 'password': password, - 'passphrase': passphrase, - 'seed': seed, - 'nettype': nettype, - 'restoreHeight': restoreHeight - }); - -Future restoreFromKeys( - {required String path, - required String password, - required String language, - required String address, - required String viewKey, - required String spendKey, - int nettype = 0, - int restoreHeight = 0}) async => - _restoreFromKeys({ - 'path': path, - 'password': password, - 'language': language, - 'address': address, - 'viewKey': viewKey, - 'spendKey': spendKey, - 'nettype': nettype, - 'restoreHeight': restoreHeight - }); - -Future restoreFromSpendKey( - {required String path, - required String password, - required String seed, - required String language, - required String spendKey, - int nettype = 0, - int restoreHeight = 0}) async => - _restoreFromSpendKey({ - 'path': path, - 'password': password, - 'seed': seed, - 'language': language, - 'spendKey': spendKey, - 'nettype': nettype, - 'restoreHeight': restoreHeight - }); - -bool isWalletExist({required String path}) => _isWalletExist(path); - -bool isViewOnlyBySpendKey(Pointer? wptrOverride) => int.tryParse(monero.Wallet_secretSpendKey(wptrOverride ?? wptr!)) == 0; +bool isViewOnlyBySpendKey(Wallet2Wallet? wallet) => int.tryParse((wallet??currentWallet!).secretSpendKey()) == 0; diff --git a/cw_monero/lib/ledger.dart b/cw_monero/lib/ledger.dart index 219d45bb5..d931b58d0 100644 --- a/cw_monero/lib/ledger.dart +++ b/cw_monero/lib/ledger.dart @@ -7,26 +7,26 @@ import 'package:cw_core/utils/print_verbose.dart'; import 'package:ffi/ffi.dart'; import 'package:ledger_flutter_plus/ledger_flutter_plus.dart'; import 'package:ledger_flutter_plus/ledger_flutter_plus_dart.dart'; -import 'package:monero/monero.dart' as monero; +import 'package:monero/src/wallet2.dart'; LedgerConnection? gLedger; Timer? _ledgerExchangeTimer; Timer? _ledgerKeepAlive; -void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) { +void enableLedgerExchange(Wallet2Wallet wallet, LedgerConnection connection) { _ledgerExchangeTimer?.cancel(); _ledgerExchangeTimer = Timer.periodic(Duration(milliseconds: 1), (_) async { - final ledgerRequestLength = monero.Wallet_getSendToDeviceLength(ptr); - final ledgerRequest = monero.Wallet_getSendToDevice(ptr) + final ledgerRequestLength = wallet.getSendToDeviceLength(); + final ledgerRequest = wallet.getSendToDevice() .cast() .asTypedList(ledgerRequestLength); if (ledgerRequestLength > 0) { _ledgerKeepAlive?.cancel(); final Pointer emptyPointer = malloc(0); - monero.Wallet_setDeviceSendData( - ptr, emptyPointer.cast(), 0); + wallet.setDeviceSendData( + emptyPointer.cast(), 0); malloc.free(emptyPointer); _logLedgerCommand(ledgerRequest, false); @@ -45,8 +45,8 @@ void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) { result.asTypedList(response.length)[i] = response[i]; } - monero.Wallet_setDeviceReceivedData( - ptr, result.cast(), response.length); + wallet.setDeviceReceivedData( + result.cast(), response.length); malloc.free(result); keepAlive(connection); } diff --git a/cw_monero/lib/monero_account_list.dart b/cw_monero/lib/monero_account_list.dart index c9a48a939..4190a4a1e 100644 --- a/cw_monero/lib/monero_account_list.dart +++ b/cw_monero/lib/monero_account_list.dart @@ -4,7 +4,7 @@ import 'package:cw_monero/api/wallet_manager.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/account.dart'; import 'package:cw_monero/api/account_list.dart' as account_list; -import 'package:monero/monero.dart' as monero; +import 'package:monero/src/monero.dart'; part 'monero_account_list.g.dart'; @@ -50,32 +50,32 @@ abstract class MoneroAccountListBase with Store { List getAll() { final allAccounts = account_list.getAllAccount(); final currentCount = allAccounts.length; - cachedAccounts[account_list.wptr!.address] ??= []; + cachedAccounts[account_list.currentWallet!.ffiAddress()] ??= []; - if (cachedAccounts[account_list.wptr!.address]!.length == currentCount) { - return cachedAccounts[account_list.wptr!.address]!; + if (cachedAccounts[account_list.currentWallet!.ffiAddress()]!.length == currentCount) { + return cachedAccounts[account_list.currentWallet!.ffiAddress()]!; } - cachedAccounts[account_list.wptr!.address] = allAccounts.map((accountRow) { - final balance = monero.SubaddressAccountRow_getUnlockedBalance(accountRow); + cachedAccounts[account_list.currentWallet!.ffiAddress()] = allAccounts.map((accountRow) { + final balance = accountRow.getUnlockedBalance(); return Account( - id: monero.SubaddressAccountRow_getRowId(accountRow), - label: monero.SubaddressAccountRow_getLabel(accountRow), - balance: moneroAmountToString(amount: monero.Wallet_amountFromString(balance)), + id: accountRow.getRowId(), + label: accountRow.getLabel(), + balance: moneroAmountToString(amount: account_list.currentWallet!.amountFromString(balance)), ); }).toList(); - return cachedAccounts[account_list.wptr!.address]!; + return cachedAccounts[account_list.currentWallet!.ffiAddress()]!; } - Future addAccount({required String label}) async { - await account_list.addAccount(label: label); + void addAccount({required String label}) { + account_list.addAccount(label: label); update(); } - Future setLabelAccount({required int accountIndex, required String label}) async { - await account_list.setLabelForAccount(accountIndex: accountIndex, label: label); + void setLabelAccount({required int accountIndex, required String label}) { + account_list.setLabelForAccount(accountIndex: accountIndex, label: label); update(); } diff --git a/cw_monero/lib/monero_unspent.dart b/cw_monero/lib/monero_unspent.dart index 224e33b2a..a88b98323 100644 --- a/cw_monero/lib/monero_unspent.dart +++ b/cw_monero/lib/monero_unspent.dart @@ -1,7 +1,7 @@ import 'package:cw_core/unspent_transaction_output.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_monero/api/coins_info.dart'; -import 'package:monero/monero.dart' as monero; +import 'package:monero/src/monero.dart'; class MoneroUnspent extends Unspent { static Future fromUnspent(String address, String hash, String keyImage, int value, bool isFrozen, bool isUnlocked) async { diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 69c894908..7a4d943fe 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -39,6 +39,7 @@ import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:ledger_flutter_plus/ledger_flutter_plus.dart'; import 'package:mobx/mobx.dart'; +import 'package:monero/src/monero.dart' as m; import 'package:monero/monero.dart' as monero; part 'monero_wallet.g.dart'; @@ -84,7 +85,7 @@ abstract class MoneroWalletBase extends WalletBase isEnabledAutoGenerateSubaddress, (bool enabled) { @@ -139,7 +140,7 @@ abstract class MoneroWalletBase extends WalletBase - transactionHistory.transactions.values.firstOrNull?.height ?? monero.Wallet_getRefreshFromBlockHeight(wptr!); + transactionHistory.transactions.values.firstOrNull?.height ?? currentWallet?.getRefreshFromBlockHeight(); monero_wallet.SyncListener? _listener; ReactionDisposer? _onAccountChangeReaction; @@ -169,7 +170,7 @@ abstract class MoneroWalletBase extends WalletBase connectToNode({required Node node}) async { try { syncStatus = ConnectingSyncStatus(); - await monero_wallet.setupNode( + await monero_wallet.setupNodeSync( address: node.uri.toString(), login: node.login, password: node.password, @@ -237,10 +247,10 @@ abstract class MoneroWalletBase extends WalletBase stopSync() async { if (isBackgroundSyncRunning) { printV("Stopping background sync"); - monero.Wallet_store(wptr!); - monero.Wallet_stopBackgroundSync(wptr!, ''); - monero_wallet.store(); + currentWallet!.store(); + currentWallet!.stopBackgroundSync(''); + currentWallet!.store(); isBackgroundSyncRunning = false; } await save(); @@ -269,9 +279,9 @@ abstract class MoneroWalletBase extends WalletBase stopBackgroundSync(String password) async { if (isBackgroundSyncRunning) { printV("Stopping background sync"); - monero.Wallet_store(wptr!); - monero.Wallet_stopBackgroundSync(wptr!, password); - monero.Wallet_store(wptr!); + currentWallet!.store(); + currentWallet!.stopBackgroundSync(password); + currentWallet!.store(); isBackgroundSyncRunning = false; } } @@ -308,44 +318,44 @@ abstract class MoneroWalletBase extends WalletBase submitTransactionUR(String ur) async { - final retStatus = monero.Wallet_submitTransactionUR(wptr!, ur); - final status = monero.Wallet_status(wptr!); + final retStatus = currentWallet!.submitTransactionUR(ur); + final status = currentWallet!.status(); if (status != 0) { - final err = monero.Wallet_errorString(wptr!); + final err = currentWallet!.errorString(); throw MoneroTransactionCreationException("unable to broadcast signed transaction: $err"); } return retStatus; } bool importKeyImagesUR(String ur) { - final retStatus = monero.Wallet_importKeyImagesUR(wptr!, ur); - final status = monero.Wallet_status(wptr!); + final retStatus = currentWallet!.importKeyImagesUR(ur); + final status = currentWallet!.status(); if (status != 0) { - final err = monero.Wallet_errorString(wptr!); + final err = currentWallet!.errorString(); throw Exception("unable to import key images: $err"); } return retStatus; } String exportOutputsUR(bool all) { - final str = monero.Wallet_exportOutputsUR(wptr!, all: all); - final status = monero.Wallet_status(wptr!); + final str = currentWallet!.exportOutputsUR(all: all); + final status = currentWallet!.status(); if (status != 0) { - final err = monero.Wallet_errorString(wptr!); + final err = currentWallet!.errorString(); throw MoneroTransactionCreationException("unable to export UR: $err"); } return str; } bool needExportOutputs(int amount) { - if (int.tryParse(monero.Wallet_secretSpendKey(wptr!)) != 0) { + if (int.tryParse(currentWallet!.secretSpendKey()) != 0) { return false; } // viewOnlyBalance - balance that we can spend // TODO(mrcyjanek): remove hasUnknownKeyImages when we cleanup coin control - return (monero.Wallet_viewOnlyBalance(wptr!, + return (currentWallet!.viewOnlyBalance( accountIndex: walletAddresses.account!.id) < amount) || - monero.Wallet_hasUnknownKeyImages(wptr!); + currentWallet!.hasUnknownKeyImages(); } @override @@ -425,12 +435,13 @@ abstract class MoneroWalletBase extends WalletBase _askForUpdateTransactionHistory() async => - await updateTransactions(); - int _getUnlockedBalance() => monero_wallet.getUnlockedBalance( accountIndex: walletAddresses.account!.id); @@ -852,13 +874,13 @@ abstract class MoneroWalletBase extends WalletBase.delayed(Duration(seconds: 1)); } catch (e) { @@ -917,8 +939,7 @@ abstract class MoneroWalletBase extends WalletBase info.id == WalletBase.idFor(name, getType())); final wallet = MoneroWallet( @@ -217,13 +220,23 @@ class MoneroWalletService extends WalletService< if (openedWalletsByPath["$path/$wallet"] != null) { // NOTE: this is realistically only required on windows. printV("closing wallet"); - final wmaddr = wmPtr.address; - final waddr = openedWalletsByPath["$path/$wallet"]!.address; - // await Isolate.run(() { - monero.WalletManager_closeWallet( - Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), false); - // }); + final w = openedWalletsByPath["$path/$wallet"]!; + final wmaddr = wmPtr.ffiAddress(); + final waddr = w.ffiAddress(); openedWalletsByPath.remove("$path/$wallet"); + if (Platform.isWindows) { + await Isolate.run(() { + monero.WalletManager_closeWallet( + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true); + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr)); + }); + } else { + unawaited(Isolate.run(() { + monero.WalletManager_closeWallet( + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true); + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr)); + })); + } printV("wallet closed"); } @@ -263,7 +276,7 @@ class MoneroWalletService extends WalletService< {bool? isTestnet}) async { try { final path = await pathForWallet(name: credentials.name, type: getType()); - await monero_wallet_manager.restoreFromKeys( + monero_wallet_manager.restoreWalletFromKeys( path: path, password: credentials.password!, language: credentials.language, @@ -293,9 +306,13 @@ class MoneroWalletService extends WalletService< final password = credentials.password; final height = credentials.height; - if (wptr == null) monero_wallet_manager.createWalletPointer(); + if (currentWallet == null) { + final tmpWptr = monero_wallet_manager.createWalletPointer(); + enableLedgerExchange(tmpWptr, credentials.ledgerConnection); + } else { + enableLedgerExchange(currentWallet!, credentials.ledgerConnection); + } - enableLedgerExchange(wptr!, credentials.ledgerConnection); await monero_wallet_manager.restoreWalletFromHardwareWallet( path: path, password: password!, @@ -352,7 +369,7 @@ class MoneroWalletService extends WalletService< try { final path = await pathForWallet(name: credentials.name, type: getType()); - monero_wallet_manager.restoreFromSeed( + monero_wallet_manager.restoreWalletFromSeedSync( path: path, password: credentials.password!, passphrase: credentials.passphrase, @@ -393,7 +410,7 @@ class MoneroWalletService extends WalletService< walletInfo.isRecovery = true; walletInfo.restoreHeight = height; - monero_wallet_manager.restoreFromSeed( + monero_wallet_manager.restoreWalletFromSeedSync( path: path, password: password, passphrase: '', @@ -401,12 +418,12 @@ class MoneroWalletService extends WalletService< restoreHeight: height, ); - monero.Wallet_setCacheAttribute(wptr!, + currentWallet!.setCacheAttribute( key: "cakewallet.seed.bip39", value: mnemonic); - monero.Wallet_setCacheAttribute(wptr!, + currentWallet!.setCacheAttribute( key: "cakewallet.passphrase", value: passphrase ?? ''); - monero.Wallet_store(wptr!); + currentWallet!.store(); final wallet = MoneroWallet( walletInfo: walletInfo, @@ -472,7 +489,7 @@ class MoneroWalletService extends WalletService< walletInfo.isRecovery = true; walletInfo.restoreHeight = height; - await monero_wallet_manager.restoreFromSpendKey( + monero_wallet_manager.restoreWalletFromSpendKeySync( path: path, password: password, seed: seed, @@ -481,8 +498,8 @@ class MoneroWalletService extends WalletService< spendKey: spendKey); - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase??''); + currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed); + currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase??''); final wallet = MoneroWallet( walletInfo: walletInfo, @@ -529,7 +546,7 @@ class MoneroWalletService extends WalletService< if (walletFilesExist(path)) await repairOldAndroidWallet(name); await monero_wallet_manager - .openWalletAsync({'path': path, 'password': password}); + .openWallet(path: path, password: password); final walletInfo = walletInfoSource.values .firstWhere((info) => info.id == WalletBase.idFor(name, getType())); final wallet = MoneroWallet( diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index 278226b07..e48cf1dca 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -573,8 +573,8 @@ packages: dependency: "direct main" description: path: "impls/monero.dart" - ref: "84e52393e395d75f449bcd81e23028889538118f" - resolved-ref: "84e52393e395d75f449bcd81e23028889538118f" + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 + resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 url: "https://github.com/mrcyjanek/monero_c" source: git version: "0.0.0" diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml index 0e1537ee0..90d0b85ab 100644 --- a/cw_monero/pubspec.yaml +++ b/cw_monero/pubspec.yaml @@ -27,7 +27,7 @@ dependencies: monero: git: url: https://github.com/mrcyjanek/monero_c - ref: 84e52393e395d75f449bcd81e23028889538118f + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 path: impls/monero.dart mutex: ^3.1.0 ledger_flutter_plus: ^1.4.1 diff --git a/cw_wownero/pubspec.lock b/cw_wownero/pubspec.lock index 404060e43..66fd1fc56 100644 --- a/cw_wownero/pubspec.lock +++ b/cw_wownero/pubspec.lock @@ -480,8 +480,8 @@ packages: dependency: "direct main" description: path: "impls/monero.dart" - ref: "84e52393e395d75f449bcd81e23028889538118f" - resolved-ref: "84e52393e395d75f449bcd81e23028889538118f" + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 + resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 url: "https://github.com/mrcyjanek/monero_c" source: git version: "0.0.0" diff --git a/cw_wownero/pubspec.yaml b/cw_wownero/pubspec.yaml index b35f36071..7b9ec4c41 100644 --- a/cw_wownero/pubspec.yaml +++ b/cw_wownero/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: monero: git: url: https://github.com/mrcyjanek/monero_c - ref: 84e52393e395d75f449bcd81e23028889538118f # monero_c hash + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 # monero_c hash path: impls/monero.dart mutex: ^3.1.0 diff --git a/cw_zano/pubspec.lock b/cw_zano/pubspec.lock index 50835ed3f..b5486e0c1 100644 --- a/cw_zano/pubspec.lock +++ b/cw_zano/pubspec.lock @@ -485,8 +485,8 @@ packages: dependency: "direct main" description: path: "impls/monero.dart" - ref: "84e52393e395d75f449bcd81e23028889538118f" - resolved-ref: "84e52393e395d75f449bcd81e23028889538118f" + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 + resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 url: "https://github.com/mrcyjanek/monero_c" source: git version: "0.0.0" diff --git a/cw_zano/pubspec.yaml b/cw_zano/pubspec.yaml index 9bf9c66eb..ff2c1e9a6 100644 --- a/cw_zano/pubspec.yaml +++ b/cw_zano/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: monero: git: url: https://github.com/mrcyjanek/monero_c - ref: 84e52393e395d75f449bcd81e23028889538118f # monero_c hash + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 # monero_c hash path: impls/monero.dart dev_dependencies: flutter_test: diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 50ca9d6de..3d39d589e 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -208,42 +208,40 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock_plus/ios" SPEC CHECKSUMS: - connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d + connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90 - cw_decred: 9c0e1df74745b51a1289ec5e91fb9e24b68fa14a - cw_mweb: 22cd01dfb8ad2d39b15332006f22046aaa8352a3 - device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7 - device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 - devicelocale: 35ba84dc7f45f527c3001535d8c8d104edd5d926 + cw_decred: a02cf30175a46971c1e2fa22c48407534541edc6 + cw_mweb: 3aea2fb35b2bd04d8b2d21b83216f3b8fb768d85 + device_display_brightness: 04374ebd653619292c1d996f00f42877ea19f17f + device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89 + devicelocale: bd64aa714485a8afdaded0892c1e7d5b7f680cf8 DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 - fast_scanner: 44c00940355a51258cd6c2085734193cd23d95bc - file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + fast_scanner: 2cb1ad3e69e645e9980fb4961396ce5804caa3e3 + file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 - flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb - flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f - flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 - flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be - fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f - in_app_review: a31b5257259646ea78e0e35fc914979b0031d011 - integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 + flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99 + flutter_local_authentication: 989278c681612f1ee0e36019e149137f114b9d7f + flutter_mailer: 3a8cd4f36c960fb04528d5471097270c19fec1c4 + flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418 + fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 + in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457 + integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 - package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 - reown_yttrium: c0e87e5965fa60a3559564cc35cffbba22976089 + package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d + ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8 - sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986 - share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12 + sensitive_clipboard: 161e9abc3d56b3131309d8a321eb4690a803c16b + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + sp_scanner: b1bc9321690980bdb44bba7ec85d5543e716d1b5 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 - uni_links: d97da20c7701486ba192624d99bffaaffcfc298a - universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6 - url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe - wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e - YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966 + uni_links: ed8c961e47ed9ce42b6d91e1de8049e38a4b3152 + universal_ble: ff19787898040d721109c6324472e5dd4bc86adc + url_launcher_ios: 694010445543906933d732453a59da0a173ae33d + wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index 49f366808..382b1d6c2 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -32,23 +32,28 @@ class WalletLoadingService { Future renameWallet(WalletType type, String name, String newName, {String? password}) async { - final walletService = walletServiceFactory.call(type); - final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name)); + try { + final walletService = walletServiceFactory.call(type); + final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name)); - // Save the current wallet's password to the new wallet name's key - await keyService.saveWalletPassword(walletName: newName, password: walletPassword); - // Delete previous wallet name from keyService to keep only new wallet's name - // otherwise keeps duplicate (old and new names) - await keyService.deleteWalletPassword(walletName: name); + // Save the current wallet's password to the new wallet name's key + await keyService.saveWalletPassword(walletName: newName, password: walletPassword); - await walletService.rename(name, walletPassword, newName); + await walletService.rename(name, walletPassword, newName); + // Delete previous wallet name from keyService to keep only new wallet's name + // otherwise keeps duplicate (old and new names) + await keyService.deleteWalletPassword(walletName: name); - // set shared preferences flag based on previous wallet name - if (type == WalletType.monero) { - final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name); - final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false; - final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName); - await sharedPreferences.setBool(newNameKey, isPasswordUpdated); + // set shared preferences flag based on previous wallet name + if (type == WalletType.monero) { + final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name); + final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false; + final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName); + await sharedPreferences.setBool(newNameKey, isPasswordUpdated); + } + } catch (error, stack) { + await ExceptionHandler.resetLastPopupDate(); + await ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack)); } } diff --git a/lib/monero/cw_monero.dart b/lib/monero/cw_monero.dart index b6be123c2..329c87338 100644 --- a/lib/monero/cw_monero.dart +++ b/lib/monero/cw_monero.dart @@ -39,14 +39,14 @@ class CWMoneroAccountList extends MoneroAccountList { @override Future addAccount(Object wallet, {required String label}) async { final moneroWallet = wallet as MoneroWallet; - await moneroWallet.walletAddresses.accountList.addAccount(label: label); + moneroWallet.walletAddresses.accountList.addAccount(label: label); } @override Future setLabelAccount(Object wallet, {required int accountIndex, required String label}) async { final moneroWallet = wallet as MoneroWallet; - await moneroWallet.walletAddresses.accountList + moneroWallet.walletAddresses.accountList .setLabelAccount(accountIndex: accountIndex, label: label); } } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 00353f117..b60533130 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -271,10 +271,19 @@ abstract class DashboardViewModelBase with Store { }); _transactionDisposer?.reaction.dispose(); - _transactionDisposer = reaction( - (_) => appStore.wallet!.transactionHistory.transactions.length, - _transactionDisposerCallback, - ); + _transactionDisposer = reaction((_) { + final length = appStore.wallet!.transactionHistory.transactions.length; + if (length == 0) { + return 0; + } + int confirmations = 1; + if (![WalletType.solana, WalletType.tron].contains(wallet.type)) { + try { + confirmations = appStore.wallet!.transactionHistory.transactions.values.first.confirmations + 1; + } catch (_) {} + } + return length * confirmations; + }, _transactionDisposerCallback); if (hasSilentPayments) { silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet); @@ -891,8 +900,19 @@ abstract class DashboardViewModelBase with Store { _transactionDisposer?.reaction.dispose(); - _transactionDisposer = reaction((_) => appStore.wallet!.transactionHistory.transactions.length, - _transactionDisposerCallback); + _transactionDisposer = reaction((_) { + final length = appStore.wallet!.transactionHistory.transactions.length; + if (length == 0) { + return 0; + } + int confirmations = 1; + if (![WalletType.solana, WalletType.tron].contains(wallet.type)) { + try { + confirmations = appStore.wallet!.transactionHistory.transactions.values.first.confirmations + 1; + } catch (_) {} + } + return length * confirmations; + }, _transactionDisposerCallback); } @action diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index b16edabd2..01905ba34 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/core/address_validator.dart'; import 'package:cake_wallet/core/amount_validator.dart'; @@ -591,7 +593,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor } final sharedPreferences = await SharedPreferences.getInstance(); await sharedPreferences.setString(PreferencesKey.backgroundSyncLastTrigger(wallet.name), DateTime.now().add(Duration(minutes: 1)).toIso8601String()); - + unawaited(wallet.fetchTransactions()); state = TransactionCommitted(); } catch (e) { state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency)); diff --git a/scripts/prepare_moneroc.sh b/scripts/prepare_moneroc.sh index 133c0ab2f..b49ad2c34 100755 --- a/scripts/prepare_moneroc.sh +++ b/scripts/prepare_moneroc.sh @@ -8,7 +8,7 @@ if [[ ! -d "monero_c/.git" ]]; then git clone https://github.com/mrcyjanek/monero_c --branch master monero_c cd monero_c - git checkout 84e52393e395d75f449bcd81e23028889538118f + git checkout b335585a7fb94b315eb52bd88f2da6d3489fa508 git reset --hard git submodule update --init --force --recursive ./apply_patches.sh monero