diff --git a/.gitignore b/.gitignore index efa6e25e7..597ed4be4 100644 --- a/.gitignore +++ b/.gitignore @@ -101,21 +101,7 @@ pubspec.yaml # FVM Version Cache .fvm/ -android/app/src/main/jniLibs/arm64-v8a/libwownero_wallet2_api_c.so -android/app/src/main/jniLibs/arm64-v8a/libmonero_wallet2_api_c.so -android/app/src/main/jniLibs/armeabi-v7a/libmonero_wallet2_api_c.so -android/app/src/main/jniLibs/armeabi-v7a/libwownero_wallet2_api_c.so -android/app/src/main/jniLibs/x86_64/libmonero_wallet2_api_c.so -android/app/src/main/jniLibs/x86_64/libwownero_wallet2_api_c.so -macos/monero_wallet2_api_c.dylib -macos/wownero_wallet2_api_c.dylib -/macos/monero_libwallet2_api_c.dylib -/macos/wownero_libwallet2_api_c.dylib -/ios/monero_libwallet2_api_c.dylib -/ios/wownero_libwallet2_api_c.dylib -/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so -/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so -/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so -/android/app/src/main/jniLibs/arm64-v8a/libwownero_libwallet2_api_c.so -/android/app/src/main/jniLibs/armeabi-v7a/libwownero_libwallet2_api_c.so -/android/app/src/main/jniLibs/x86_64/libwownero_libwallet2_api_c.so +scripts/linux/build/libsecret/subprojects/gi-docgen/.meson-subproject-wrap-hash.txt + +crypto_plugins/cs_monero/built_outputs +crypto_plugins/cs_monero/build diff --git a/.gitmodules b/.gitmodules index 925be21c0..6051a5f9b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,12 @@ [submodule "crypto_plugins/flutter_libepiccash"] path = crypto_plugins/flutter_libepiccash url = https://github.com/cypherstack/flutter_libepiccash.git -[submodule "crypto_plugins/flutter_libmonero"] - path = crypto_plugins/flutter_libmonero - url = https://github.com/cypherstack/flutter_libmonero.git [submodule "crypto_plugins/flutter_liblelantus"] path = crypto_plugins/flutter_liblelantus url = https://github.com/cypherstack/flutter_liblelantus.git [submodule "crypto_plugins/frostdart"] path = crypto_plugins/frostdart url = https://github.com/cypherstack/frostdart +[submodule "crypto_plugins/cs_monero"] + path = crypto_plugins/cs_monero + url = https://github.com/cypherstack/cs_monero diff --git a/android/app/src/main/jniLibs/arm64-v8a/.gitkeep b/android/app/src/main/jniLibs/arm64-v8a/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep b/android/app/src/main/jniLibs/armeabi-v7a/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/android/app/src/main/jniLibs/x86_64/.gitkeep b/android/app/src/main/jniLibs/x86_64/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/crypto_plugins/cs_monero b/crypto_plugins/cs_monero new file mode 160000 index 000000000..fee51e29e --- /dev/null +++ b/crypto_plugins/cs_monero @@ -0,0 +1 @@ +Subproject commit fee51e29e97ee8c1f57082b126173eeaf39a79f1 diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero deleted file mode 160000 index 66369ef5b..000000000 --- a/crypto_plugins/flutter_libmonero +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 66369ef5b432e4d58e76b6cc4f91a5e24eb6b1ea diff --git a/ios/MoneroWallet.framework/.gitignore b/ios/MoneroWallet.framework/.gitignore deleted file mode 100644 index 38de9b351..000000000 --- a/ios/MoneroWallet.framework/.gitignore +++ /dev/null @@ -1 +0,0 @@ -MoneroWallet \ No newline at end of file diff --git a/ios/MoneroWallet.framework/Info.plist b/ios/MoneroWallet.framework/Info.plist deleted file mode 100644 index de2be3211..000000000 --- a/ios/MoneroWallet.framework/Info.plist +++ /dev/null @@ -1,57 +0,0 @@ - - - - - BuildMachineOSBuild - 23E224 - CFBundleDevelopmentRegion - en - CFBundleExecutable - MoneroWallet - CFBundleIdentifier - com.cypherstack.MoneroWallet - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - MoneroWallet - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ??? - CFBundleSupportedPlatforms - - iPhoneOS - - CFBundleVersion - 1 - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 21E210 - DTPlatformName - iphoneos - DTPlatformVersion - 17.4 - DTSDKBuild - 21E210 - DTSDKName - iphoneos17.4 - DTXcode - 1530 - DTXcodeBuild - 15E204a - MinimumOSVersion - 16.0 - UIDeviceFamily - - 1 - 2 - - UIRequiredDeviceCapabilities - - arm64 - - - diff --git a/ios/WowneroWallet.framework/.gitignore b/ios/WowneroWallet.framework/.gitignore deleted file mode 100644 index 98afbebec..000000000 --- a/ios/WowneroWallet.framework/.gitignore +++ /dev/null @@ -1 +0,0 @@ -WowneroWallet \ No newline at end of file diff --git a/ios/WowneroWallet.framework/Info.plist b/ios/WowneroWallet.framework/Info.plist deleted file mode 100644 index d1d435920..000000000 --- a/ios/WowneroWallet.framework/Info.plist +++ /dev/null @@ -1,57 +0,0 @@ - - - - - BuildMachineOSBuild - 23E224 - CFBundleDevelopmentRegion - en - CFBundleExecutable - WowneroWallet - CFBundleIdentifier - com.cypherstack.WowneroWallet - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - WowneroWallet - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ??? - CFBundleSupportedPlatforms - - iPhoneOS - - CFBundleVersion - 1 - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 21E210 - DTPlatformName - iphoneos - DTPlatformVersion - 17.4 - DTSDKBuild - 21E210 - DTSDKName - iphoneos17.4 - DTXcode - 1530 - DTXcodeBuild - 15E204a - MinimumOSVersion - 16.0 - UIDeviceFamily - - 1 - 2 - - UIRequiredDeviceCapabilities - - arm64 - - - diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 1e4c670e1..c71826fbc 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -10,7 +10,7 @@ import 'dart:isolate'; -import 'package:cw_core/wallet_info.dart' as xmr; +import 'package:compat/compat.dart' as lib_monero_compat; import 'package:hive/hive.dart' show Box; import 'package:hive/src/hive_impl.dart'; import 'package:mutex/mutex.dart'; @@ -71,7 +71,7 @@ class DB { Box? _boxTradesV2; Box? _boxTradeNotes; Box? _boxFavoriteWallets; - Box? _walletInfoSource; + Box? _walletInfoSource; Box? _boxPrefs; Box? _boxTradeLookup; Box? _boxDBInfo; @@ -85,7 +85,8 @@ class DB { final Map> _getSparkUsedCoinsTagsCacheBoxes = {}; // exposed for monero - Box get moneroWalletInfoBox => _walletInfoSource!; + Box get moneroWalletInfoBox => + _walletInfoSource!; // mutex for stack backup final mutex = Mutex(); @@ -147,8 +148,8 @@ class DB { _boxTradesV2 = await hive.openBox(boxNameTradesV2); _boxTradeNotes = await hive.openBox(boxNameTradeNotes); _boxTradeLookup = await hive.openBox(boxNameTradeLookup); - _walletInfoSource = - await hive.openBox(xmr.WalletInfo.boxName); + _walletInfoSource = await hive.openBox( + lib_monero_compat.WalletInfo.boxName); _boxFavoriteWallets = await hive.openBox(boxNameFavoriteWallets); await Future.wait([ diff --git a/lib/main.dart b/lib/main.dart index 3dc8f53bc..30edb2bbb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,11 +13,8 @@ import 'dart:io'; import 'dart:math'; import 'package:coinlib_flutter/coinlib_flutter.dart'; -import 'package:cw_core/node.dart'; -import 'package:cw_core/pathForWallet.dart'; -import 'package:cw_core/unspent_coins_info.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cw_core/wallet_type.dart'; +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -92,10 +89,11 @@ void main(List args) async { StackFileSystem.setDesktopOverrideDir(args.last); } - // Tell flutter_libmonero how to get access to the application dir - FS.setApplicationRootDirectoryFunction( - StackFileSystem.applicationRootDirectory, - ); + // no longer requried + // // Tell flutter_libmonero how to get access to the application dir + // FS.setApplicationRootDirectoryFunction( + // StackFileSystem.applicationRootDirectory, + // ); // TODO set any other external libs file paths (bad external lib design workaround) final loadCoinlibFuture = loadCoinlib(); @@ -170,15 +168,18 @@ void main(List args) async { // node model adapter DB.instance.hive.registerAdapter(NodeModelAdapter()); - DB.instance.hive.registerAdapter(NodeAdapter()); + // DB.instance.hive.registerAdapter(NodeAdapter()); - if (!DB.instance.hive.isAdapterRegistered(WalletInfoAdapter().typeId)) { - DB.instance.hive.registerAdapter(WalletInfoAdapter()); + if (!DB.instance.hive + .isAdapterRegistered(lib_monero_compat.WalletInfoAdapter().typeId)) { + DB.instance.hive.registerAdapter(lib_monero_compat.WalletInfoAdapter()); } - DB.instance.hive.registerAdapter(WalletTypeAdapter()); + DB.instance.hive.registerAdapter(lib_monero_compat.WalletTypeAdapter()); - DB.instance.hive.registerAdapter(UnspentCoinsInfoAdapter()); + // DB.instance.hive.registerAdapter(UnspentCoinsInfoAdapter()); + + lib_monero.Logging.useLogger = true; DB.instance.hive.init( (await StackFileSystem.applicationHiveDirectory()).path, diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 0b7fcc37b..cd0115128 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -67,7 +67,7 @@ class _RestoreOptionsViewState extends ConsumerState { late final TextEditingController passwordController; final bool _nextEnabled = true; - DateTime _restoreFromDate = DateTime.fromMillisecondsSinceEpoch(0); + DateTime? _restoreFromDate; bool hidePassword = true; bool _expandedAdavnced = false; diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index 1e0b259fd..984f774ab 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -16,10 +16,10 @@ import 'dart:math'; import 'package:bip39/bip39.dart' as bip39; import 'package:bip39/src/wordlists/english.dart' as bip39wordlist; +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_libmonero/monero/monero.dart' as libxmr; -import 'package:flutter_libmonero/wownero/wownero.dart' as libwow; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; @@ -86,7 +86,7 @@ class RestoreWalletView extends ConsumerStatefulWidget { final CryptoCurrency coin; final String mnemonicPassphrase; final int seedWordsLength; - final DateTime restoreFromDate; + final DateTime? restoreFromDate; final bool enableLelantusScanning; final BarcodeScannerInterface barcodeScanner; @@ -181,7 +181,7 @@ class _RestoreWalletViewState extends ConsumerState { if (widget.coin is Monero) { switch (widget.seedWordsLength) { case 25: - return libxmr.monero.getMoneroWordList("English").contains(word); + return lib_monero.getMoneroWordList("English").contains(word); case 16: return Monero.sixteenWordsWordList.contains(word); default: @@ -189,7 +189,7 @@ class _RestoreWalletViewState extends ConsumerState { } } if (widget.coin is Wownero) { - final wowneroWordList = libwow.wownero.getWowneroWordList( + final wowneroWordList = lib_monero.getWowneroWordList( "English", seedWordsLength: widget.seedWordsLength, ); @@ -219,29 +219,35 @@ class _RestoreWalletViewState extends ConsumerState { int height = 0; String? otherDataJsonString; - if (widget.coin is Monero) { - height = libxmr.monero.getHeigthByDate(date: widget.restoreFromDate); - } else if (widget.coin is Wownero) { - height = libwow.wownero.getHeightByDate(date: widget.restoreFromDate); + if (widget.restoreFromDate != null) { + if (widget.coin is Monero) { + height = lib_monero_compat.getMoneroHeigthByDate( + date: widget.restoreFromDate!, + ); + } + if (widget.coin is Wownero) { + height = lib_monero_compat.getWowneroHeightByDate( + date: widget.restoreFromDate!, + ); + } + if (height < 0) { + height = 0; + } } - // todo: wait until this implemented - // else if (widget.coin is Wownero) { - // height = wownero.getHeightByDate(date: widget.restoreFromDate); - // } // TODO: make more robust estimate of date maybe using https://explorer.epic.tech/api-index if (widget.coin is Epiccash) { - final int secondsSinceEpoch = - widget.restoreFromDate.millisecondsSinceEpoch ~/ 1000; - const int epicCashFirstBlock = 1565370278; - const double overestimateSecondsPerBlock = 61; - final int chosenSeconds = secondsSinceEpoch - epicCashFirstBlock; - final int approximateHeight = - chosenSeconds ~/ overestimateSecondsPerBlock; - //todo: check if print needed - // debugPrint( - // "approximate height: $approximateHeight chosen_seconds: $chosenSeconds"); - height = approximateHeight; + if (widget.restoreFromDate != null) { + final int secondsSinceEpoch = + widget.restoreFromDate!.millisecondsSinceEpoch ~/ 1000; + const int epicCashFirstBlock = 1565370278; + const double overestimateSecondsPerBlock = 61; + final int chosenSeconds = secondsSinceEpoch - epicCashFirstBlock; + final int approximateHeight = + chosenSeconds ~/ overestimateSecondsPerBlock; + + height = approximateHeight; + } if (height < 0) { height = 0; } diff --git a/lib/pages/exchange_view/send_from_view.dart b/lib/pages/exchange_view/send_from_view.dart index 643768f19..f499775b1 100644 --- a/lib/pages/exchange_view/send_from_view.dart +++ b/lib/pages/exchange_view/send_from_view.dart @@ -35,7 +35,7 @@ import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; import '../../wallets/models/tx_data.dart'; import '../../wallets/wallet/impl/firo_wallet.dart'; -import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../widgets/background.dart'; import '../../widgets/conditional_parent.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -277,7 +277,7 @@ class _SendFromCardState extends ConsumerState { // access to this screen but this is needed to get past an error that // would occur only to lead to another error which is why xmr/wow wallets // don't have access to this screen currently - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { await wallet.init(); await wallet.open(); } diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index 3c52c8efc..711a25a45 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -26,7 +26,7 @@ import '../../utilities/biometrics.dart'; import '../../utilities/flutter_secure_storage_interface.dart'; import '../../utilities/show_loading.dart'; import '../../utilities/text_styles.dart'; -import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../widgets/background.dart'; import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/custom_buttons/blue_text_button.dart'; @@ -102,7 +102,7 @@ class _LockscreenViewState extends ConsumerState { final wallet = ref.read(pWallets).getWallet(walletId); final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet.init().then((value) async => await (wallet).open()); } else { diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index cd7d5f01a..584d868d2 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -11,10 +11,10 @@ import 'dart:async'; import 'dart:io'; -import 'package:cw_core/monero_transaction_priority.dart'; import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:tuple/tuple.dart'; @@ -470,22 +470,22 @@ class _SendViewState extends ConsumerState { Amount fee; if (coin is Monero) { - MoneroTransactionPriority specialMoneroId; + lib_monero.TransactionPriority specialMoneroId; switch (ref.read(feeRateTypeStateProvider.state).state) { case FeeRateType.fast: - specialMoneroId = MoneroTransactionPriority.fast; + specialMoneroId = lib_monero.TransactionPriority.high; break; case FeeRateType.average: - specialMoneroId = MoneroTransactionPriority.regular; + specialMoneroId = lib_monero.TransactionPriority.medium; break; case FeeRateType.slow: - specialMoneroId = MoneroTransactionPriority.slow; + specialMoneroId = lib_monero.TransactionPriority.normal; break; default: throw ArgumentError("custom fee not available for monero"); } - fee = await wallet.estimateFeeFor(amount, specialMoneroId.raw!); + fee = await wallet.estimateFeeFor(amount, specialMoneroId.value); cachedFees[amount] = ref.read(pAmountFormatter(coin)).format( fee, withUnitName: true, diff --git a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart index a70098c03..0ca0dba47 100644 --- a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart +++ b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart @@ -8,9 +8,10 @@ * */ -import 'package:cw_core/monero_transaction_priority.dart'; import 'package:flutter/material.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../models/paymint/fee_object_model.dart'; import '../../../providers/providers.dart'; import '../../../providers/ui/fee_rate_type_state_provider.dart'; @@ -90,7 +91,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.fast.raw!, + lib_monero.TransactionPriority.high.value, ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { @@ -127,7 +128,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.regular.raw!, + lib_monero.TransactionPriority.medium.value, ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { @@ -163,7 +164,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.slow.raw!, + lib_monero.TransactionPriority.normal.value, ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { diff --git a/lib/pages/settings_views/global_settings_view/about_view.dart b/lib/pages/settings_views/global_settings_view/about_view.dart index f4c3eb7ec..b72c3222c 100644 --- a/lib/pages/settings_views/global_settings_view/about_view.dart +++ b/lib/pages/settings_views/global_settings_view/about_view.dart @@ -272,48 +272,48 @@ class AboutView extends ConsumerWidget { const SizedBox( height: 12, ), - if (AppConfig.coins.whereType().isNotEmpty) - FutureBuilder( - future: GitStatus.getMoneroCommitStatus(), - builder: ( - context, - AsyncSnapshot snapshot, - ) { - CommitStatus stateOfCommit = - CommitStatus.notLoaded; - - if (snapshot.connectionState == - ConnectionState.done && - snapshot.hasData) { - stateOfCommit = snapshot.data!; - } - return RoundedWhiteContainer( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - children: [ - Text( - "Monero Build Commit", - style: STextStyles.titleBold12(context), - ), - const SizedBox( - height: 4, - ), - SelectableText( - GitStatus.moneroCommit, - style: GitStatus.styleForStatus( - stateOfCommit, - context, - ), - ), - ], - ), - ); - }, - ), - const SizedBox( - height: 12, - ), + // if (AppConfig.coins.whereType().isNotEmpty) + // FutureBuilder( + // future: GitStatus.getMoneroCommitStatus(), + // builder: ( + // context, + // AsyncSnapshot snapshot, + // ) { + // CommitStatus stateOfCommit = + // CommitStatus.notLoaded; + // + // if (snapshot.connectionState == + // ConnectionState.done && + // snapshot.hasData) { + // stateOfCommit = snapshot.data!; + // } + // return RoundedWhiteContainer( + // child: Column( + // crossAxisAlignment: + // CrossAxisAlignment.stretch, + // children: [ + // Text( + // "Monero Build Commit", + // style: STextStyles.titleBold12(context), + // ), + // const SizedBox( + // height: 4, + // ), + // SelectableText( + // GitStatus.moneroCommit, + // style: GitStatus.styleForStatus( + // stateOfCommit, + // context, + // ), + // ), + // ], + // ), + // ); + // }, + // ), + // const SizedBox( + // height: 12, + // ), RoundedWhiteContainer( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart index 81297430e..92e5cc256 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart @@ -18,7 +18,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS; -import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS; +// import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS; @@ -313,8 +313,8 @@ class _DebugViewState extends ConsumerState { FIRO_VERSIONS.getPluginVersion(); final String epicCashCommit = EPIC_VERSIONS.getPluginVersion(); - final String moneroCommit = - MONERO_VERSIONS.getPluginVersion(); + // final String moneroCommit = + // MONERO_VERSIONS.getPluginVersion(); final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); final deviceInfo = @@ -347,7 +347,7 @@ class _DebugViewState extends ConsumerState { "appName": appName, "firoCommit": firoCommit, "epicCashCommit": epicCashCommit, - "moneroCommit": moneroCommit, + // "moneroCommit": moneroCommit, "deviceInfoMap": deviceInfoMap, "errorLogs": errorLogs, }; diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart index 478440c99..7888635b7 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart @@ -29,7 +29,7 @@ import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; -import '../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -222,7 +222,7 @@ class _AddEditNodeViewState extends ConsumerState { // strip unused path String address = formData.host!; - if (coin is CwBasedInterface) { + if (coin is LibMoneroWallet) { if (address.startsWith("http")) { final uri = Uri.parse(address); address = "${uri.scheme}://${uri.host}"; @@ -837,7 +837,7 @@ class _NodeFormState extends ConsumerState { } else { enableSSLCheckbox = true; } - } else if (widget.coin is CwBasedInterface) { + } else if (widget.coin is LibMoneroWallet) { if (newValue.startsWith("https://")) { _useSSL = true; } else if (newValue.startsWith("http://")) { @@ -1052,7 +1052,7 @@ class _NodeFormState extends ConsumerState { ), ], ), - if (widget.coin is CwBasedInterface) + if (widget.coin is LibMoneroWallet) Row( children: [ GestureDetector( diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart index a4d7d69fe..32430d228 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart @@ -54,8 +54,8 @@ import '../../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../../../../wallets/wallet/impl/epiccash_wallet.dart'; import '../../../../../wallets/wallet/impl/monero_wallet.dart'; import '../../../../../wallets/wallet/impl/wownero_wallet.dart'; +import '../../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../../../wallets/wallet/wallet.dart'; -import '../../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart'; @@ -503,7 +503,7 @@ abstract class SWB { Future? restoringFuture; - if (!(wallet is CwBasedInterface || wallet is EpiccashWallet)) { + if (!(wallet is LibMoneroWallet || wallet is EpiccashWallet)) { if (wallet is BitcoinFrostWallet) { restoringFuture = wallet.recover( isRescan: false, diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart index 6985bb00c..22d3a4073 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart @@ -36,7 +36,7 @@ import '../../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../../wallets/wallet/impl/epiccash_wallet.dart'; -import '../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../widgets/background.dart'; @@ -307,7 +307,7 @@ class _WalletSettingsViewState extends ConsumerState { KeyDataInterface? keyData; if (wallet is ExtendedKeysInterface) { keyData = await wallet.getXPrivs(); - } else if (wallet is CwBasedInterface) { + } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); } diff --git a/lib/pages/special/firo_rescan_recovery_error_dialog.dart b/lib/pages/special/firo_rescan_recovery_error_dialog.dart index 92d226d4b..fa59d841a 100644 --- a/lib/pages/special/firo_rescan_recovery_error_dialog.dart +++ b/lib/pages/special/firo_rescan_recovery_error_dialog.dart @@ -12,7 +12,7 @@ import '../../utilities/assets.dart'; import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; -import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../widgets/background.dart'; @@ -270,7 +270,7 @@ class _FiroRescanRecoveryErrorViewState KeyDataInterface? keyData; if (wallet is ExtendedKeysInterface) { keyData = await wallet.getXPrivs(); - } else if (wallet is CwBasedInterface) { + } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); } diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index ab91cc759..48d0d1257 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -13,7 +13,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import '../../wallet_view/wallet_view.dart'; + import '../../../pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart'; import '../../../providers/providers.dart'; import '../../../themes/coin_icon_provider.dart'; @@ -26,9 +26,10 @@ import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../wallets/crypto_currency/coins/firo.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; -import '../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../widgets/coin_card.dart'; import '../../../widgets/conditional_parent.dart'; +import '../../wallet_view/wallet_view.dart'; class FavoriteCard extends ConsumerStatefulWidget { const FavoriteCard({ @@ -117,7 +118,7 @@ class _FavoriteCardState extends ConsumerState { final wallet = ref.read(pWallets).getWallet(walletId); final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet.init().then((value) async => await (wallet).open()); } else { diff --git a/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart b/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart index 5602d2419..3ac87eb2e 100644 --- a/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart +++ b/lib/pages/wallets_view/sub_widgets/wallet_list_item.dart @@ -24,7 +24,7 @@ import '../../../utilities/show_loading.dart'; import '../../../utilities/text_styles.dart'; import '../../../utilities/util.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; -import '../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../widgets/dialogs/tor_warning_dialog.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../wallet_view/wallet_view.dart'; @@ -84,7 +84,7 @@ class WalletListItem extends ConsumerWidget { .wallets .firstWhere((e) => e.info.coin == coin); final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet.init().then((value) async => await (wallet).open()); } else { diff --git a/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart b/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart index e440af8fe..7771c33f5 100644 --- a/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart +++ b/lib/pages_desktop_specific/my_stack_view/coin_wallets_table.dart @@ -10,7 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'wallet_view/desktop_wallet_view.dart'; + import '../../providers/global/active_wallet_provider.dart'; import '../../providers/global/wallets_provider.dart'; import '../../themes/stack_colors.dart'; @@ -18,9 +18,10 @@ import '../../utilities/constants.dart'; import '../../utilities/show_loading.dart'; import '../../utilities/util.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; -import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../widgets/rounded_container.dart'; import '../../widgets/wallet_info_row/wallet_info_row.dart'; +import 'wallet_view/desktop_wallet_view.dart'; class CoinWalletsTable extends ConsumerWidget { const CoinWalletsTable({ @@ -81,7 +82,7 @@ class CoinWalletsTable extends ConsumerWidget { final wallet = ref.read(pWallets).getWallet(walletIds[i]); final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet .init() .then((value) async => await (wallet).open()); diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart index 3b25f0dad..6326a6474 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart @@ -8,9 +8,9 @@ * */ -import 'package:cw_core/monero_transaction_priority.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; @@ -86,7 +86,7 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.fast.raw!, + lib_monero.TransactionPriority.high.value, ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { @@ -136,7 +136,7 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.regular.raw!, + lib_monero.TransactionPriority.medium.value, ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { @@ -186,7 +186,7 @@ class _DesktopFeeDropDownState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.slow.raw!, + lib_monero.TransactionPriority.normal.value, ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index ce3ab2b1f..80dea6795 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -10,11 +10,11 @@ import 'dart:async'; -import 'package:cw_core/monero_transaction_priority.dart'; import 'package:decimal/decimal.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -1860,7 +1860,7 @@ class _DesktopSendState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.regular.raw!, + lib_monero.TransactionPriority.medium.value, ); ref .read(feeSheetSessionCacheProvider) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart index e332c4543..3ae249dfb 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart @@ -23,7 +23,7 @@ import '../../../../utilities/assets.dart'; import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; -import '../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; +import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../../widgets/desktop/desktop_dialog.dart'; @@ -106,7 +106,7 @@ class _UnlockWalletKeysDesktopState KeyDataInterface? keyData; if (wallet is ExtendedKeysInterface) { keyData = await wallet.getXPrivs(); - } else if (wallet is CwBasedInterface) { + } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); } @@ -353,7 +353,7 @@ class _UnlockWalletKeysDesktopState KeyDataInterface? keyData; if (wallet is ExtendedKeysInterface) { keyData = await wallet.getXPrivs(); - } else if (wallet is CwBasedInterface) { + } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); } diff --git a/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart b/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart index 34c8a8dba..98a7dec34 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart @@ -434,63 +434,63 @@ class DesktopAboutView extends ConsumerWidget { ); }, ), - if (AppConfig.coins - .whereType() - .isNotEmpty) - FutureBuilder( - future: GitStatus - .getMoneroCommitStatus(), - builder: ( - context, - AsyncSnapshot - snapshot, - ) { - CommitStatus stateOfCommit = - CommitStatus.notLoaded; - - if (snapshot.connectionState == - ConnectionState - .done && - snapshot.hasData) { - stateOfCommit = - snapshot.data!; - } - return Column( - mainAxisSize: - MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - "Monero Build Commit", - style: STextStyles - .desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of( - context, - ) - .extension< - StackColors>()! - .textDark, - ), - ), - const SizedBox( - height: 2, - ), - SelectableText( - GitStatus.moneroCommit, - style: GitStatus - .styleForStatus( - stateOfCommit, - context, - ), - ), - ], - ); - }, - ), + // if (AppConfig.coins + // .whereType() + // .isNotEmpty) + // FutureBuilder( + // future: GitStatus + // .getMoneroCommitStatus(), + // builder: ( + // context, + // AsyncSnapshot + // snapshot, + // ) { + // CommitStatus stateOfCommit = + // CommitStatus.notLoaded; + // + // if (snapshot.connectionState == + // ConnectionState + // .done && + // snapshot.hasData) { + // stateOfCommit = + // snapshot.data!; + // } + // return Column( + // mainAxisSize: + // MainAxisSize.min, + // crossAxisAlignment: + // CrossAxisAlignment + // .start, + // children: [ + // Text( + // "Monero Build Commit", + // style: STextStyles + // .desktopTextExtraExtraSmall( + // context, + // ).copyWith( + // color: Theme.of( + // context, + // ) + // .extension< + // StackColors>()! + // .textDark, + // ), + // ), + // const SizedBox( + // height: 2, + // ), + // SelectableText( + // GitStatus.moneroCommit, + // style: GitStatus + // .styleForStatus( + // stateOfCommit, + // context, + // ), + // ), + // ], + // ); + // }, + // ), ], ), const SizedBox(height: 35), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index b449525b6..6c58b600e 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -1456,7 +1456,7 @@ class RouteGenerator { case RestoreWalletView.routeName: if (args - is Tuple6) { + is Tuple6) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => RestoreWalletView( diff --git a/lib/services/wallets.dart b/lib/services/wallets.dart index c4e75bde8..d7b93cf33 100644 --- a/lib/services/wallets.dart +++ b/lib/services/wallets.dart @@ -10,8 +10,7 @@ import 'dart:async'; -import 'package:flutter_libmonero/monero/monero.dart' as monero; -import 'package:flutter_libmonero/wownero/wownero.dart' as wownero; +import 'package:compat/compat.dart' as lib_monero_compat; import 'package:isar/isar.dart'; import '../app_config.dart'; @@ -21,11 +20,12 @@ import '../utilities/enums/sync_type_enum.dart'; import '../utilities/flutter_secure_storage_interface.dart'; import '../utilities/logger.dart'; import '../utilities/prefs.dart'; +import '../utilities/stack_file_system.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; import '../wallets/isar/models/wallet_info.dart'; import '../wallets/wallet/impl/epiccash_wallet.dart'; +import '../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../wallets/wallet/wallet.dart'; -import '../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import 'event_bus/events/wallet_added_event.dart'; import 'event_bus/global_event_bus.dart'; import 'node_service.dart'; @@ -86,15 +86,19 @@ class Wallets { await secureStorage.delete(key: Wallet.privateKeyKey(walletId: walletId)); if (info.coin is Wownero) { - final wowService = wownero.wownero - .createWowneroWalletService(DB.instance.moneroWalletInfoBox); - await wowService.remove(walletId); + await lib_monero_compat.deleteWalletFiles( + name: walletId, + type: lib_monero_compat.WalletType.wownero, + appRoot: await StackFileSystem.applicationRootDirectory(), + ); Logging.instance .log("monero wallet: $walletId deleted", level: LogLevel.Info); } else if (info.coin is Monero) { - final xmrService = monero.monero - .createMoneroWalletService(DB.instance.moneroWalletInfoBox); - await xmrService.remove(walletId); + await lib_monero_compat.deleteWalletFiles( + name: walletId, + type: lib_monero_compat.WalletType.monero, + appRoot: await StackFileSystem.applicationRootDirectory(), + ); Logging.instance .log("monero wallet: $walletId deleted", level: LogLevel.Info); } else if (info.coin is Epiccash) { @@ -220,7 +224,7 @@ class Wallets { final shouldSetAutoSync = shouldAutoSyncAll || walletIdsToEnableAutoSync.contains(walletInfo.walletId); - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); } else { walletInitFutures.add( @@ -327,7 +331,7 @@ class Wallets { nodeService: nodeService, prefs: prefs, ).then((wallet) { - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); walletIdCompleter.complete("dummy_ignore"); @@ -457,7 +461,7 @@ class Wallets { nodeService: nodeService, prefs: prefs, ).then((wallet) { - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); walletIdCompleter.complete("dummy_ignore"); @@ -577,7 +581,7 @@ class Wallets { walletIdsToEnableAutoSync.contains(wallet.walletId); if (isDesktop) { - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); } else { walletInitFutures.add( diff --git a/lib/utilities/git_status.dart b/lib/utilities/git_status.dart index 818660427..093bc39af 100644 --- a/lib/utilities/git_status.dart +++ b/lib/utilities/git_status.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_libepiccash/git_versions.dart' as epic_versions; -import 'package:flutter_libmonero/git_versions.dart' as monero_versions; +// import 'package:flutter_libmonero/git_versions.dart' as monero_versions; import 'package:http/http.dart'; import 'package:lelantus/git_versions.dart' as firo_versions; @@ -20,7 +20,7 @@ enum CommitStatus { isHead, isOldCommit, notACommit, notLoaded } abstract class GitStatus { static String get firoCommit => firo_versions.getPluginVersion(); static String get epicCashCommit => epic_versions.getPluginVersion(); - static String get moneroCommit => monero_versions.getPluginVersion(); + // static String get moneroCommit => monero_versions.getPluginVersion(); static String get appCommitHash => AppConfig.commitHash; @@ -78,31 +78,31 @@ abstract class GitStatus { return _cachedEpicStatus!; } - - static CommitStatus? _cachedMoneroStatus; - static Future getMoneroCommitStatus() async { - if (_cachedMoneroStatus != null) { - return _cachedMoneroStatus!; - } - - final List results = await Future.wait([ - _doesCommitExist("cypherstack", "flutter_libmonero", moneroCommit), - _isHeadCommit("cypherstack", "flutter_libmonero", "main", moneroCommit), - ]); - - final commitExists = results[0]; - final commitIsHead = results[1]; - - if (commitExists && commitIsHead) { - _cachedMoneroStatus = CommitStatus.isHead; - } else if (commitExists) { - _cachedMoneroStatus = CommitStatus.isOldCommit; - } else { - _cachedMoneroStatus = CommitStatus.notACommit; - } - - return _cachedMoneroStatus!; - } + // + // static CommitStatus? _cachedMoneroStatus; + // static Future getMoneroCommitStatus() async { + // if (_cachedMoneroStatus != null) { + // return _cachedMoneroStatus!; + // } + // + // final List results = await Future.wait([ + // _doesCommitExist("cypherstack", "flutter_libmonero", moneroCommit), + // _isHeadCommit("cypherstack", "flutter_libmonero", "main", moneroCommit), + // ]); + // + // final commitExists = results[0]; + // final commitIsHead = results[1]; + // + // if (commitExists && commitIsHead) { + // _cachedMoneroStatus = CommitStatus.isHead; + // } else if (commitExists) { + // _cachedMoneroStatus = CommitStatus.isOldCommit; + // } else { + // _cachedMoneroStatus = CommitStatus.notACommit; + // } + // + // return _cachedMoneroStatus!; + // } static TextStyle styleForStatus(CommitStatus status, BuildContext context) { final Color color; diff --git a/lib/wallets/models/tx_data.dart b/lib/wallets/models/tx_data.dart index 99a28a138..231cb99d7 100644 --- a/lib/wallets/models/tx_data.dart +++ b/lib/wallets/models/tx_data.dart @@ -1,5 +1,4 @@ -import 'package:cw_monero/pending_monero_transaction.dart'; -import 'package:cw_wownero/pending_wownero_transaction.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:tezart/tezart.dart' as tezart; import 'package:web3dart/web3dart.dart' as web3dart; @@ -49,11 +48,8 @@ class TxData { final BigInt? chainId; final BigInt? feeInWei; - // wownero specific - final PendingWowneroTransaction? pendingWowneroTransaction; - - // monero specific - final PendingMoneroTransaction? pendingMoneroTransaction; + // wownero and monero specific + final lib_monero.PendingTransaction? pendingTransaction; // firo lelantus specific final int? jMintValue; @@ -104,8 +100,7 @@ class TxData { this.nonce, this.chainId, this.feeInWei, - this.pendingWowneroTransaction, - this.pendingMoneroTransaction, + this.pendingTransaction, this.jMintValue, this.spendCoinIndexes, this.height, @@ -196,8 +191,7 @@ class TxData { int? nonce, BigInt? chainId, BigInt? feeInWei, - PendingWowneroTransaction? pendingWowneroTransaction, - PendingMoneroTransaction? pendingMoneroTransaction, + lib_monero.PendingTransaction? pendingTransaction, int? jMintValue, List? spendCoinIndexes, int? height, @@ -241,10 +235,7 @@ class TxData { nonce: nonce ?? this.nonce, chainId: chainId ?? this.chainId, feeInWei: feeInWei ?? this.feeInWei, - pendingWowneroTransaction: - pendingWowneroTransaction ?? this.pendingWowneroTransaction, - pendingMoneroTransaction: - pendingMoneroTransaction ?? this.pendingMoneroTransaction, + pendingTransaction: pendingTransaction ?? this.pendingTransaction, jMintValue: jMintValue ?? this.jMintValue, spendCoinIndexes: spendCoinIndexes ?? this.spendCoinIndexes, height: height ?? this.height, @@ -284,8 +275,7 @@ class TxData { 'nonce: $nonce, ' 'chainId: $chainId, ' 'feeInWei: $feeInWei, ' - 'pendingWowneroTransaction: $pendingWowneroTransaction, ' - 'pendingMoneroTransaction: $pendingMoneroTransaction, ' + 'pendingTransaction: $pendingTransaction, ' 'jMintValue: $jMintValue, ' 'spendCoinIndexes: $spendCoinIndexes, ' 'height: $height, ' diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index c3c3ea02a..0bc11c3e3 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -1,206 +1,51 @@ import 'dart:async'; -import 'dart:io'; -import 'dart:math'; -import 'package:cw_core/monero_transaction_priority.dart'; -import 'package:cw_core/node.dart'; -import 'package:cw_core/pending_transaction.dart'; -import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/transaction_direction.dart'; -import 'package:cw_core/utxo.dart' as cw; -import 'package:cw_core/wallet_base.dart'; -import 'package:cw_core/wallet_credentials.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; -import 'package:cw_monero/monero_wallet.dart'; -import 'package:cw_monero/monero_wallet_service.dart'; -import 'package:cw_monero/pending_monero_transaction.dart'; -import 'package:decimal/decimal.dart'; -import 'package:flutter_libmonero/core/wallet_creation_service.dart'; -import 'package:flutter_libmonero/monero/monero.dart' as xmr_dart; -import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output; -import 'package:isar/isar.dart'; -import 'package:mutex/mutex.dart'; -import 'package:tuple/tuple.dart'; +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; -import '../../../db/hive/db.dart'; -import '../../../models/isar/models/blockchain_data/address.dart'; -import '../../../models/isar/models/blockchain_data/transaction.dart'; -import '../../../models/isar/models/blockchain_data/utxo.dart'; -import '../../../models/keys/cw_key_data.dart'; -import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; -import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; -import '../../../services/event_bus/global_event_bus.dart'; -import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; -import '../../../utilities/enums/fee_rate_type_enum.dart'; -import '../../../utilities/logger.dart'; import '../../crypto_currency/crypto_currency.dart'; -import '../../models/tx_data.dart'; -import '../intermediate/cryptonote_wallet.dart'; -import '../wallet.dart'; -import '../wallet_mixin_interfaces/cw_based_interface.dart'; +import '../intermediate/lib_monero_wallet.dart'; -class MoneroWallet extends CryptonoteWallet with CwBasedInterface { - MoneroWallet(CryptoCurrencyNetwork network) : super(Monero(network)) { - final bus = GlobalEventBus.instance; - - // Listen for tor status changes. - _torStatusListener = bus.on().listen( - (event) async { - switch (event.newStatus) { - case TorConnectionStatus.connecting: - if (!_torConnectingLock.isLocked) { - await _torConnectingLock.acquire(); - } - _requireMutex = true; - break; - - case TorConnectionStatus.connected: - case TorConnectionStatus.disconnected: - if (_torConnectingLock.isLocked) { - _torConnectingLock.release(); - } - _requireMutex = false; - break; - } - }, - ); - - // Listen for tor preference changes. - _torPreferenceListener = bus.on().listen( - (event) async { - await updateNode(); - }, - ); - - // Potentially dangerous hack. See comments in _startInit() - _startInit(); - } - - // cw based wallet listener to handle synchronization of utxo frozen states - late final StreamSubscription> _streamSub; - Future _startInit() async { - // Delay required as `mainDB` is not initialized in constructor. - // This is a hack and could lead to a race condition. - Future.delayed(const Duration(seconds: 2), () { - _streamSub = mainDB.isar.utxos - .where() - .walletIdEqualTo(walletId) - .watch(fireImmediately: true) - .listen((utxos) async { - await onUTXOsCHanged(utxos); - await updateBalance(shouldUpdateUtxos: false); - }); - }); - } - - @override - MoneroWalletBase? cwWalletBase; - - @override - MoneroWalletService? cwWalletService; - - @override - Address addressFor({required int index, int account = 0}) { - final String address = (cwWalletBase as MoneroWalletBase) - .getTransactionAddress(account, index); - - final newReceivingAddress = Address( - walletId: walletId, - derivationIndex: index, - derivationPath: null, - value: address, - publicKey: [], - type: AddressType.cryptonote, - subType: AddressSubType.receiving, - ); - - return newReceivingAddress; - } - - @override - Future open() async { - String? password; - try { - password = await cwKeysStorage.getWalletPassword(walletName: walletId); - } catch (e, s) { - throw Exception("Password not found $e, $s"); - } - - bool wasNull = false; - - if (cwWalletBase == null) { - wasNull = true; - // cwWalletBaseT?.close(); - cwWalletBase ??= (await cwWalletService!.openWallet(walletId, password)) - as MoneroWalletBase; - - cwWalletBase?.onNewBlock ??= onNewBlock; - cwWalletBase?.onNewTransaction ??= onNewTransaction; - cwWalletBase?.syncStatusChanged ??= syncStatusChanged; - - await updateNode(); - } - - Address? currentAddress = await getCurrentReceivingAddress(); - if (currentAddress == null) { - currentAddress = addressFor(index: 0); - await mainDB.updateOrPutAddresses([currentAddress]); - } - if (info.cachedReceivingAddress != currentAddress.value) { - await info.updateReceivingAddress( - newAddress: currentAddress.value, - isar: mainDB.isar, - ); - } - - if (wasNull) { - await cwWalletBase?.startSync(); - } else { - cwWalletBase?.wallet.startListeners(); - } - unawaited(refresh()); - - autoSaveTimer?.cancel(); - autoSaveTimer = Timer.periodic( - const Duration(seconds: 193), - (_) async => await cwWalletBase?.save(), - ); - } +class MoneroWallet extends LibMoneroWallet { + MoneroWallet(CryptoCurrencyNetwork network) + : super( + Monero(network), + lib_monero_compat.WalletType.monero, + ); @override Future estimateFeeFor(Amount amount, int feeRate) async { - if (cwWalletBase == null || cwWalletBase?.syncStatus is! SyncedSyncStatus) { + if (libMoneroWallet == null || + syncStatus is! lib_monero_compat.SyncedSyncStatus) { return Amount.zeroWith( fractionDigits: cryptoCurrency.fractionDigits, ); } - MoneroTransactionPriority priority; + lib_monero.TransactionPriority priority; switch (feeRate) { case 1: - priority = MoneroTransactionPriority.regular; + priority = lib_monero.TransactionPriority.low; break; case 2: - priority = MoneroTransactionPriority.medium; + priority = lib_monero.TransactionPriority.medium; break; case 3: - priority = MoneroTransactionPriority.fast; + priority = lib_monero.TransactionPriority.high; break; case 4: - priority = MoneroTransactionPriority.fastest; + priority = lib_monero.TransactionPriority.last; break; case 0: default: - priority = MoneroTransactionPriority.slow; + priority = lib_monero.TransactionPriority.normal; break; } int approximateFee = 0; await estimateFeeMutex.protect(() async { - approximateFee = cwWalletBase!.calculateEstimatedFee( + approximateFee = await libMoneroWallet!.estimateFee( priority, amount.raw.toInt(), ); @@ -213,573 +58,49 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface { } @override - Future pingCheck() async { - return await cwWalletBase?.isConnected() ?? false; - } + bool walletExists(String path) => lib_monero.MoneroWallet.isWalletExist(path); @override - Future updateNode() async { - final node = getCurrentNode(); - - String host = Uri.parse(node.host).host; - if (host.isEmpty) { - host = node.host; - } - ({InternetAddress host, int port})? proxy; - if (prefs.useTor) { - proxy = TorService.sharedInstance.getProxyInfo(); - } - if (_requireMutex) { - await _torConnectingLock.protect(() async { - await cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.monero, - trusted: node.trusted ?? false, - useSSL: node.useSSL, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); - }); - } else { - await cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.monero, - trusted: node.trusted ?? false, - useSSL: node.useSSL, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); - } - - return; - } - - @override - Future getKeys() async { - final base = cwWalletBase; - - if (base == null || base.walletInfo.name != walletId) { - return null; - } - - return CWKeyData( - walletId: walletId, - publicViewKey: base.keys.publicViewKey, - privateViewKey: base.keys.privateViewKey, - publicSpendKey: base.keys.publicSpendKey, - privateSpendKey: base.keys.privateSpendKey, + void loadWallet({ + required String path, + required String password, + }) { + libMoneroWallet = lib_monero.MoneroWallet.loadWallet( + path: path, + password: password, ); } @override - Future updateTransactions() async { - final base = cwWalletBase; - - if (base == null || base.walletInfo.name != walletId) { - return; - } - await base.updateTransactions(); - final transactions = base.transactionHistory?.transactions; - - // final cachedTransactions = - // DB.instance.get(boxName: walletId, key: 'latest_tx_model') - // as TransactionData?; - // int latestTxnBlockHeight = - // DB.instance.get(boxName: walletId, key: "storedTxnDataHeight") - // as int? ?? - // 0; - // - // final txidsList = DB.instance - // .get(boxName: walletId, key: "cachedTxids") as List? ?? - // []; - // - // final Set cachedTxids = Set.from(txidsList); - - // TODO: filter to skip cached + confirmed txn processing in next step - // final unconfirmedCachedTransactions = - // cachedTransactions?.getAllTransactions() ?? {}; - // unconfirmedCachedTransactions - // .removeWhere((key, value) => value.confirmedStatus); - // - // if (cachedTransactions != null) { - // for (final tx in allTxHashes.toList(growable: false)) { - // final txHeight = tx["height"] as int; - // if (txHeight > 0 && - // txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) { - // if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) { - // allTxHashes.remove(tx); - // } - // } - // } - // } - - final List> txnsData = []; - - if (transactions != null) { - for (final tx in transactions.entries) { - Address? address; - TransactionType type; - if (tx.value.direction == TransactionDirection.incoming) { - final addressInfo = tx.value.additionalInfo; - - final addressString = cwWalletBase?.getTransactionAddress( - addressInfo!['accountIndex'] as int, - addressInfo['addressIndex'] as int, - ); - - if (addressString != null) { - address = await mainDB - .getAddresses(walletId) - .filter() - .valueEqualTo(addressString) - .findFirst(); - } - - type = TransactionType.incoming; - } else { - // txn.address = ""; - type = TransactionType.outgoing; - } - - final txn = Transaction( - walletId: walletId, - txid: tx.value.id, - timestamp: (tx.value.date.millisecondsSinceEpoch ~/ 1000), - type: type, - subType: TransactionSubType.none, - amount: tx.value.amount ?? 0, - amountString: Amount( - rawValue: BigInt.from(tx.value.amount ?? 0), - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), - fee: tx.value.fee ?? 0, - height: tx.value.height, - isCancelled: false, - isLelantus: false, - slateId: null, - otherData: null, - nonce: null, - inputs: [], - outputs: [], - numberOfMessages: null, - ); - - txnsData.add(Tuple2(txn, address)); - } - } - - await mainDB.isar.writeTxn(() async { - await mainDB.isar.transactions - .where() - .walletIdEqualTo(walletId) - .deleteAll(); - for (final data in txnsData) { - final tx = data.item1; - - // save transaction - await mainDB.isar.transactions.put(tx); - - if (data.item2 != null) { - final address = await mainDB.getAddress(walletId, data.item2!.value); - - // check if address exists in db and add if it does not - if (address == null) { - await mainDB.isar.addresses.put(data.item2!); - } - - // link and save address - tx.address.value = address ?? data.item2!; - await tx.address.save(); - } - } - }); - } - - @override - Future init({bool? isRestore}) async { - cwWalletService ??= xmr_dart.monero - .createMoneroWalletService(DB.instance.moneroWalletInfoBox) - as MoneroWalletService; - - if (!(await cwWalletService!.isWalletExist(walletId)) && - isRestore != true) { - WalletInfo walletInfo; - WalletCredentials credentials; - try { - final dirPath = - await pathForWalletDir(name: walletId, type: WalletType.monero); - final path = - await pathForWallet(name: walletId, type: WalletType.monero); - credentials = xmr_dart.monero.createMoneroNewWalletCredentials( - name: walletId, - language: "English", - ); - - walletInfo = WalletInfo.external( - id: WalletBase.idFor(walletId, WalletType.monero), - name: walletId, - type: WalletType.monero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - address: '', - ); - credentials.walletInfo = walletInfo; - - final _walletCreationService = WalletCreationService( - secureStorage: secureStorageInterface, - walletService: cwWalletService!, - keyService: cwKeysStorage, - type: WalletType.monero, - ); - // To restore from a seed - final wallet = await _walletCreationService.create(credentials); - - // subtract a couple days to ensure we have a buffer for SWB - final bufferedCreateHeight = xmr_dart.monero.getHeigthByDate( - date: DateTime.now().subtract(const Duration(days: 2)), - ); - - await info.updateRestoreHeight( - newRestoreHeight: bufferedCreateHeight, - isar: mainDB.isar, - ); - - // special case for xmr/wow. Normally mnemonic + passphrase is saved - // before wallet.init() is called - await secureStorageInterface.write( - key: Wallet.mnemonicKey(walletId: walletId), - value: wallet.seed.trim(), - ); - await secureStorageInterface.write( - key: Wallet.mnemonicPassphraseKey(walletId: walletId), - value: "", - ); - - walletInfo.restoreHeight = bufferedCreateHeight; - - walletInfo.address = wallet.walletAddresses.address; - await DB.instance - .add(boxName: WalletInfo.boxName, value: walletInfo); - - wallet.close(); - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Fatal); - cwWalletBase?.close(); - } - await updateNode(); - } - - return super.init(); - } - - @override - Future recover({required bool isRescan}) async { - if (isRescan) { - await refreshMutex.protect(() async { - // clear blockchain info - await mainDB.deleteWalletBlockchainData(walletId); - - final restoreHeight = cwWalletBase?.walletInfo.restoreHeight; - highestPercentCached = 0; - await cwWalletBase?.rescan(height: restoreHeight ?? 0); - }); - unawaited(refresh()); - return; - } - - await refreshMutex.protect(() async { - final mnemonic = await getMnemonic(); - final seedLength = mnemonic.trim().split(" ").length; - - if (seedLength != 25 && seedLength != 16) { - throw Exception("Invalid monero mnemonic length found: $seedLength"); - } - - try { - final height = max(info.restoreHeight, 0); - - await info.updateRestoreHeight( - newRestoreHeight: height, - isar: mainDB.isar, - ); - - cwWalletService = xmr_dart.monero - .createMoneroWalletService(DB.instance.moneroWalletInfoBox) - as MoneroWalletService; - WalletInfo walletInfo; - WalletCredentials credentials; - final String name = walletId; - final dirPath = - await pathForWalletDir(name: name, type: WalletType.monero); - final path = await pathForWallet(name: name, type: WalletType.monero); - credentials = - xmr_dart.monero.createMoneroRestoreWalletFromSeedCredentials( - name: name, - height: height, - mnemonic: mnemonic.trim(), - ); - try { - walletInfo = WalletInfo.external( - id: WalletBase.idFor(name, WalletType.monero), - name: name, - type: WalletType.monero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - address: '', - ); - credentials.walletInfo = walletInfo; - - final cwWalletCreationService = WalletCreationService( - secureStorage: secureStorageInterface, - walletService: cwWalletService!, - keyService: cwKeysStorage, - type: WalletType.monero, - ); - // To restore from a seed - final wallet = - await cwWalletCreationService.restoreFromSeed(credentials); - walletInfo.address = wallet.walletAddresses.address; - await DB.instance - .add(boxName: WalletInfo.boxName, value: walletInfo); - cwWalletBase?.close(); - cwWalletBase = wallet as MoneroWalletBase; - cwWalletBase?.onNewBlock = onNewBlock; - cwWalletBase?.onNewTransaction = onNewTransaction; - cwWalletBase?.syncStatusChanged = syncStatusChanged; - if (walletInfo.address != null) { - final newReceivingAddress = await getCurrentReceivingAddress() ?? - Address( - walletId: walletId, - derivationIndex: 0, - derivationPath: null, - value: walletInfo.address!, - publicKey: [], - type: AddressType.cryptonote, - subType: AddressSubType.receiving, - ); - - await mainDB.updateOrPutAddresses([newReceivingAddress]); - await info.updateReceivingAddress( - newAddress: newReceivingAddress.value, - isar: mainDB.isar, - ); - } - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Fatal); - } - await updateNode(); - - await cwWalletBase?.rescan(height: credentials.height); - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from recoverFromMnemonic(): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - }); - } - - @override - Future prepareSend({required TxData txData}) async { - try { - final feeRate = txData.feeRateType; - if (feeRate is FeeRateType) { - MoneroTransactionPriority feePriority; - switch (feeRate) { - case FeeRateType.fast: - feePriority = MoneroTransactionPriority.fast; - break; - case FeeRateType.average: - feePriority = MoneroTransactionPriority.regular; - break; - case FeeRateType.slow: - feePriority = MoneroTransactionPriority.slow; - break; - default: - throw ArgumentError("Invalid use of custom fee"); - } - - Future? awaitPendingTransaction; - try { - // check for send all - bool isSendAll = false; - final balance = await availableBalance; - if (txData.amount! == balance && - txData.recipients!.first.amount == balance) { - isSendAll = true; - } - - final List outputs = []; - for (final recipient in txData.recipients!) { - final output = monero_output.Output(cwWalletBase!); - output.address = recipient.address; - output.sendAll = isSendAll; - final String amountToSend = recipient.amount.decimal.toString(); - output.setCryptoAmount(amountToSend); - outputs.add(output); - } - - final tmp = - xmr_dart.monero.createMoneroTransactionCreationCredentials( - outputs: outputs, - priority: feePriority, - ); - - final height = await chainHeight; - final inputs = txData.utxos - ?.map( - (e) => cw.UTXO( - address: e.address!, - hash: e.txid, - keyImage: e.keyImage!, - value: e.value, - isFrozen: e.isBlocked, - isUnlocked: e.blockHeight != null && - (height - (e.blockHeight ?? 0)) >= - cryptoCurrency.minConfirms, - height: e.blockHeight ?? 0, - vout: e.vout, - spent: e.used ?? false, - coinbase: e.isCoinbase, - ), - ) - .toList(); - - await prepareSendMutex.protect(() async { - awaitPendingTransaction = cwWalletBase!.createTransaction( - tmp, - inputs: inputs, - ); - }); - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from prepareSend(): $e\n$s", - level: LogLevel.Warning, - ); - } - - final PendingMoneroTransaction pendingMoneroTransaction = - await (awaitPendingTransaction!) as PendingMoneroTransaction; - final realFee = Amount.fromDecimal( - Decimal.parse(pendingMoneroTransaction.feeFormatted), - fractionDigits: cryptoCurrency.fractionDigits, - ); - - return txData.copyWith( - fee: realFee, - pendingMoneroTransaction: pendingMoneroTransaction, - ); - } else { - throw ArgumentError("Invalid fee rate argument provided!"); - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from prepare send(): $e\n$s", - level: LogLevel.Info, + Future getCreatedWallet({ + required String path, + required String password, + }) async => + await lib_monero.MoneroWallet.create( + path: path, + password: password, + seedType: lib_monero.MoneroSeedType + .sixteen, // TODO: check we want to actually use 16 here ); - if (e.toString().contains("Incorrect unlocked balance")) { - throw Exception("Insufficient balance!"); - } else if (e is CreationTransactionException) { - throw Exception("Insufficient funds to pay for transaction fee!"); - } else { - throw Exception("Transaction failed with error code $e"); - } - } - } - @override - Future confirmSend({required TxData txData}) async { - try { - try { - await txData.pendingMoneroTransaction!.commit(); - Logging.instance.log( - "transaction ${txData.pendingMoneroTransaction!.id} has been sent", - level: LogLevel.Info, - ); - return txData.copyWith(txid: txData.pendingMoneroTransaction!.id); - } catch (e, s) { - Logging.instance.log( - "${info.name} monero confirmSend: $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from confirmSend(): $e\n$s", - level: LogLevel.Info, + Future getRestoredWallet({ + required String path, + required String password, + required String mnemonic, + int height = 0, + }) async => + await lib_monero.MoneroWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, ); - rethrow; - } - } @override - Future get availableBalance async { - try { - int runningBalance = 0; - for (final entry in cwWalletBase!.balance!.entries) { - runningBalance += entry.value.unlockedBalance; - } - return Amount( - rawValue: BigInt.from(runningBalance), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } catch (_) { - return info.cachedBalance.spendable; + void invalidSeedLengthCheck(int length) { + if (length != 25 && length != 16) { + throw Exception("Invalid monero mnemonic length found: $length"); } } - - @override - Future get totalBalance async { - try { - final balanceEntries = cwWalletBase?.balance?.entries; - if (balanceEntries != null) { - int bal = 0; - for (final element in balanceEntries) { - bal = bal + element.value.fullBalance; - } - return Amount( - rawValue: BigInt.from(bal), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } else { - final transactions = cwWalletBase!.transactionHistory!.transactions; - int transactionBalance = 0; - for (final tx in transactions!.entries) { - if (tx.value.direction == TransactionDirection.incoming) { - transactionBalance += tx.value.amount!; - } else { - transactionBalance += -tx.value.amount! - tx.value.fee!; - } - } - - return Amount( - rawValue: BigInt.from(transactionBalance), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } - } catch (_) { - return info.cachedBalance.total; - } - } - - // ============== Private ==================================================== - - StreamSubscription? _torStatusListener; - StreamSubscription? _torPreferenceListener; - - final Mutex _torConnectingLock = Mutex(); - bool _requireMutex = false; } diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index 40c8cd644..afcfee319 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -1,157 +1,52 @@ import 'dart:async'; -import 'dart:io'; -import 'dart:math'; -import 'package:cw_core/monero_transaction_priority.dart'; -import 'package:cw_core/node.dart'; -import 'package:cw_core/pending_transaction.dart'; -import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/transaction_direction.dart'; -import 'package:cw_core/utxo.dart' as cw; -import 'package:cw_core/wallet_base.dart'; -import 'package:cw_core/wallet_credentials.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; -import 'package:cw_wownero/pending_wownero_transaction.dart'; -import 'package:cw_wownero/wownero_wallet.dart'; -import 'package:cw_wownero/wownero_wallet_service.dart'; -import 'package:decimal/decimal.dart'; -import 'package:flutter_libmonero/core/wallet_creation_service.dart'; -import 'package:flutter_libmonero/view_model/send/output.dart' - as wownero_output; -import 'package:flutter_libmonero/wownero/wownero.dart' as wow_dart; -import 'package:isar/isar.dart'; -import 'package:monero/wownero.dart' as wownerodart; -import 'package:mutex/mutex.dart'; -import 'package:tuple/tuple.dart'; +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; -import '../../../db/hive/db.dart'; -import '../../../models/isar/models/blockchain_data/address.dart'; -import '../../../models/isar/models/blockchain_data/transaction.dart'; -import '../../../models/isar/models/blockchain_data/utxo.dart'; -import '../../../models/keys/cw_key_data.dart'; -import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; -import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; -import '../../../services/event_bus/global_event_bus.dart'; -import '../../../services/tor_service.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; -import '../../../utilities/logger.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; -import '../intermediate/cryptonote_wallet.dart'; -import '../wallet.dart'; -import '../wallet_mixin_interfaces/cw_based_interface.dart'; +import '../intermediate/lib_monero_wallet.dart'; -class WowneroWallet extends CryptonoteWallet with CwBasedInterface { - WowneroWallet(CryptoCurrencyNetwork network) : super(Wownero(network)) { - final bus = GlobalEventBus.instance; - - // Listen for tor status changes. - _torStatusListener = bus.on().listen( - (event) async { - switch (event.newStatus) { - case TorConnectionStatus.connecting: - if (!_torConnectingLock.isLocked) { - await _torConnectingLock.acquire(); - } - _requireMutex = true; - break; - - case TorConnectionStatus.connected: - case TorConnectionStatus.disconnected: - if (_torConnectingLock.isLocked) { - _torConnectingLock.release(); - } - _requireMutex = false; - break; - } - }, - ); - - // Listen for tor preference changes. - _torPreferenceListener = bus.on().listen( - (event) async { - await updateNode(); - }, - ); - - // Potentially dangerous hack. See comments in _startInit() - _startInit(); - } - - // cw based wallet listener to handle synchronization of utxo frozen states - late final StreamSubscription> _streamSub; - Future _startInit() async { - // Delay required as `mainDB` is not initialized in constructor. - // This is a hack and could lead to a race condition. - Future.delayed(const Duration(seconds: 2), () { - _streamSub = mainDB.isar.utxos - .where() - .walletIdEqualTo(walletId) - .watch(fireImmediately: true) - .listen((utxos) async { - await onUTXOsCHanged(utxos); - await updateBalance(shouldUpdateUtxos: false); - }); - }); - } - - @override - WowneroWalletBase? cwWalletBase; - - @override - WowneroWalletService? cwWalletService; - - @override - Address addressFor({required int index, int account = 0}) { - final String address = (cwWalletBase as WowneroWalletBase) - .getTransactionAddress(account, index); - - final newReceivingAddress = Address( - walletId: walletId, - derivationIndex: index, - derivationPath: null, - value: address, - publicKey: [], - type: AddressType.cryptonote, - subType: AddressSubType.receiving, - ); - - return newReceivingAddress; - } +class WowneroWallet extends LibMoneroWallet { + WowneroWallet(CryptoCurrencyNetwork network) + : super( + Wownero(network), + lib_monero_compat.WalletType.wownero, + ); @override Future estimateFeeFor(Amount amount, int feeRate) async { - if (cwWalletBase == null || cwWalletBase?.syncStatus is! SyncedSyncStatus) { + if (libMoneroWallet == null || + syncStatus is! lib_monero_compat.SyncedSyncStatus) { return Amount.zeroWith( fractionDigits: cryptoCurrency.fractionDigits, ); } - MoneroTransactionPriority priority; + lib_monero.TransactionPriority priority; FeeRateType feeRateType = FeeRateType.slow; switch (feeRate) { case 1: - priority = MoneroTransactionPriority.regular; + priority = lib_monero.TransactionPriority.low; feeRateType = FeeRateType.average; break; case 2: - priority = MoneroTransactionPriority.medium; + priority = lib_monero.TransactionPriority.medium; feeRateType = FeeRateType.average; break; case 3: - priority = MoneroTransactionPriority.fast; + priority = lib_monero.TransactionPriority.high; feeRateType = FeeRateType.fast; break; case 4: - priority = MoneroTransactionPriority.fastest; + priority = lib_monero.TransactionPriority.last; feeRateType = FeeRateType.fast; break; case 0: default: - priority = MoneroTransactionPriority.slow; + priority = lib_monero.TransactionPriority.normal; feeRateType = FeeRateType.slow; break; } @@ -179,7 +74,7 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { // unsure why this delay? await Future.delayed(const Duration(milliseconds: 500)); } catch (e) { - approximateFee = cwWalletBase!.calculateEstimatedFee( + approximateFee = libMoneroWallet!.estimateFee( priority, amount.raw.toInt(), ); @@ -198,634 +93,51 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface { } @override - Future pingCheck() async { - return await cwWalletBase?.isConnected() ?? false; - } + bool walletExists(String path) => + lib_monero.WowneroWallet.isWalletExist(path); @override - Future updateNode() async { - final node = getCurrentNode(); - - final host = Uri.parse(node.host).host; - ({InternetAddress host, int port})? proxy; - if (prefs.useTor) { - proxy = TorService.sharedInstance.getProxyInfo(); - } - if (_requireMutex) { - await _torConnectingLock.protect(() async { - await cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.wownero, - trusted: node.trusted ?? false, - useSSL: node.useSSL, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); - }); - } else { - await cwWalletBase?.connectToNode( - node: Node( - uri: "$host:${node.port}", - type: WalletType.wownero, - trusted: node.trusted ?? false, - useSSL: node.useSSL, - ), - socksProxyAddress: - proxy == null ? null : "${proxy.host.address}:${proxy.port}", - ); - } - - return; - } - - @override - Future getKeys() async { - final base = cwWalletBase; - - if (base == null || base.walletInfo.name != walletId) { - return null; - } - - return CWKeyData( - walletId: walletId, - publicViewKey: base.keys.publicViewKey, - privateViewKey: base.keys.privateViewKey, - publicSpendKey: base.keys.publicSpendKey, - privateSpendKey: base.keys.privateSpendKey, + void loadWallet({ + required String path, + required String password, + }) { + libMoneroWallet = lib_monero.WowneroWallet.loadWallet( + path: path, + password: password, ); } @override - Future updateTransactions() async { - final base = cwWalletBase; - - if (base == null || base.walletInfo.name != walletId) { - return; - } - await base.updateTransactions(); - final transactions = base.transactionHistory?.transactions; - - // final cachedTransactions = - // DB.instance.get(boxName: walletId, key: 'latest_tx_model') - // as TransactionData?; - // int latestTxnBlockHeight = - // DB.instance.get(boxName: walletId, key: "storedTxnDataHeight") - // as int? ?? - // 0; - // - // final txidsList = DB.instance - // .get(boxName: walletId, key: "cachedTxids") as List? ?? - // []; - // - // final Set cachedTxids = Set.from(txidsList); - - // TODO: filter to skip cached + confirmed txn processing in next step - // final unconfirmedCachedTransactions = - // cachedTransactions?.getAllTransactions() ?? {}; - // unconfirmedCachedTransactions - // .removeWhere((key, value) => value.confirmedStatus); - // - // if (cachedTransactions != null) { - // for (final tx in allTxHashes.toList(growable: false)) { - // final txHeight = tx["height"] as int; - // if (txHeight > 0 && - // txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) { - // if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) { - // allTxHashes.remove(tx); - // } - // } - // } - // } - - final List> txnsData = []; - - if (transactions != null) { - for (final tx in transactions.entries) { - Address? address; - TransactionType type; - if (tx.value.direction == TransactionDirection.incoming) { - final addressInfo = tx.value.additionalInfo; - - final addressString = cwWalletBase?.getTransactionAddress( - addressInfo!['accountIndex'] as int, - addressInfo['addressIndex'] as int, - ); - - if (addressString != null) { - address = await mainDB - .getAddresses(walletId) - .filter() - .valueEqualTo(addressString) - .findFirst(); - } - - type = TransactionType.incoming; - } else { - // txn.address = ""; - type = TransactionType.outgoing; - } - - final txn = Transaction( - walletId: walletId, - txid: tx.value.id, - timestamp: (tx.value.date.millisecondsSinceEpoch ~/ 1000), - type: type, - subType: TransactionSubType.none, - amount: tx.value.amount ?? 0, - amountString: Amount( - rawValue: BigInt.from(tx.value.amount ?? 0), - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), - fee: tx.value.fee ?? 0, - height: tx.value.height, - isCancelled: false, - isLelantus: false, - slateId: null, - otherData: null, - nonce: null, - inputs: [], - outputs: [], - numberOfMessages: null, - ); - - txnsData.add(Tuple2(txn, address)); - } - } - - await mainDB.isar.writeTxn(() async { - await mainDB.isar.transactions - .where() - .walletIdEqualTo(walletId) - .deleteAll(); - for (final data in txnsData) { - final tx = data.item1; - - // save transaction - await mainDB.isar.transactions.put(tx); - - if (data.item2 != null) { - final address = await mainDB.getAddress(walletId, data.item2!.value); - - // check if address exists in db and add if it does not - if (address == null) { - await mainDB.isar.addresses.put(data.item2!); - } - - // link and save address - tx.address.value = address ?? data.item2!; - await tx.address.save(); - } - } - }); - } - - @override - Future init({bool? isRestore}) async { - cwWalletService = wow_dart.wownero - .createWowneroWalletService(DB.instance.moneroWalletInfoBox) - as WowneroWalletService; - - if (!(await cwWalletService!.isWalletExist(walletId)) && - isRestore != true) { - WalletInfo walletInfo; - WalletCredentials credentials; - try { - final dirPath = - await pathForWalletDir(name: walletId, type: WalletType.wownero); - final path = - await pathForWallet(name: walletId, type: WalletType.wownero); - credentials = wow_dart.wownero.createWowneroNewWalletCredentials( - name: walletId, - language: "English", - seedWordsLength: 14, - ); - - walletInfo = WalletInfo.external( - id: WalletBase.idFor(walletId, WalletType.wownero), - name: walletId, - type: WalletType.wownero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - address: '', - ); - credentials.walletInfo = walletInfo; - - final _walletCreationService = WalletCreationService( - secureStorage: secureStorageInterface, - walletService: cwWalletService!, - keyService: cwKeysStorage, - type: WalletType.wownero, - ); - // To restore from a seed - final wallet = await _walletCreationService.create(credentials) - as WowneroWalletBase; - - final height = - wownerodart.Wallet_getRefreshFromBlockHeight(wallet.wallet.wptr); - - await info.updateRestoreHeight( - newRestoreHeight: height, - isar: mainDB.isar, - ); - - // special case for xmr/wow. Normally mnemonic + passphrase is saved - // before wallet.init() is called - await secureStorageInterface.write( - key: Wallet.mnemonicKey(walletId: walletId), - value: wallet.seed.trim(), - ); - await secureStorageInterface.write( - key: Wallet.mnemonicPassphraseKey(walletId: walletId), - value: "", - ); - - walletInfo.restoreHeight = height; - - walletInfo.address = wallet.walletAddresses.address; - await DB.instance - .add(boxName: WalletInfo.boxName, value: walletInfo); - - wallet.close(); - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Fatal); - cwWalletBase?.close(); - } - await updateNode(); - } - - return super.init(); - } - - @override - Future open() async { - String? password; - try { - password = await cwKeysStorage.getWalletPassword(walletName: walletId); - } catch (e, s) { - throw Exception("Password not found $e, $s"); - } - - bool wasNull = false; - - if (cwWalletBase == null) { - wasNull = true; - // cwWalletBaseT?.close(); - cwWalletBase = (await cwWalletService!.openWallet(walletId, password)) - as WowneroWalletBase; - - cwWalletBase?.onNewBlock = onNewBlock; - cwWalletBase?.onNewTransaction = onNewTransaction; - cwWalletBase?.syncStatusChanged = syncStatusChanged; - - await updateNode(); - } - - Address? currentAddress = await getCurrentReceivingAddress(); - if (currentAddress == null) { - currentAddress = addressFor(index: 0); - await mainDB.updateOrPutAddresses([currentAddress]); - } - if (info.cachedReceivingAddress != currentAddress.value) { - await info.updateReceivingAddress( - newAddress: currentAddress.value, - isar: mainDB.isar, - ); - } - - if (wasNull) { - await cwWalletBase?.startSync(); - } else { - cwWalletBase?.wallet.startListeners(); - } - unawaited(refresh()); - - autoSaveTimer = Timer.periodic( - const Duration(seconds: 193), - (_) async => await cwWalletBase?.save(), - ); - } - - @override - Future recover({required bool isRescan}) async { - if (isRescan) { - await refreshMutex.protect(() async { - // clear blockchain info - await mainDB.deleteWalletBlockchainData(walletId); - - final restoreHeight = cwWalletBase?.walletInfo.restoreHeight; - highestPercentCached = 0; - await cwWalletBase?.rescan(height: restoreHeight ?? 0); - }); - unawaited(refresh()); - return; - } - - await refreshMutex.protect(() async { - final mnemonic = await getMnemonic(); - final seedLength = mnemonic.trim().split(" ").length; - - if (!(seedLength == 14 || seedLength == 25)) { - throw Exception("Invalid wownero mnemonic length found: $seedLength"); - } - - try { - int height = info.restoreHeight; - - // extract seed height from 14 word seed - if (seedLength == 14) { - height = 0; - } else { - height = max(height, 0); - } - - await info.updateRestoreHeight( - newRestoreHeight: height, - isar: mainDB.isar, - ); - - cwWalletService = wow_dart.wownero - .createWowneroWalletService(DB.instance.moneroWalletInfoBox) - as WowneroWalletService; - WalletInfo walletInfo; - WalletCredentials credentials; - final String name = walletId; - final dirPath = - await pathForWalletDir(name: name, type: WalletType.wownero); - final path = await pathForWallet(name: name, type: WalletType.wownero); - credentials = - wow_dart.wownero.createWowneroRestoreWalletFromSeedCredentials( - name: name, - height: height, - mnemonic: mnemonic.trim(), - ); - try { - walletInfo = WalletInfo.external( - id: WalletBase.idFor(name, WalletType.wownero), - name: name, - type: WalletType.wownero, - isRecovery: false, - restoreHeight: credentials.height ?? 0, - date: DateTime.now(), - path: path, - dirPath: dirPath, - // TODO: find out what to put for address - address: '', - ); - credentials.walletInfo = walletInfo; - - final cwWalletCreationService = WalletCreationService( - secureStorage: secureStorageInterface, - walletService: cwWalletService!, - keyService: cwKeysStorage, - type: WalletType.wownero, - ); - // To restore from a seed - final wallet = await cwWalletCreationService - .restoreFromSeed(credentials) as WowneroWalletBase; - height = - wownerodart.Wallet_getRefreshFromBlockHeight(wallet.wallet.wptr); - walletInfo.address = wallet.walletAddresses.address; - walletInfo.restoreHeight = height; - await info.updateRestoreHeight( - newRestoreHeight: height, - isar: mainDB.isar, - ); - await DB.instance - .add(boxName: WalletInfo.boxName, value: walletInfo); - cwWalletBase?.close(); - cwWalletBase = wallet; - cwWalletBase?.onNewBlock = onNewBlock; - cwWalletBase?.onNewTransaction = onNewTransaction; - cwWalletBase?.syncStatusChanged = syncStatusChanged; - if (walletInfo.address != null) { - final newReceivingAddress = await getCurrentReceivingAddress() ?? - Address( - walletId: walletId, - derivationIndex: 0, - derivationPath: null, - value: walletInfo.address!, - publicKey: [], - type: AddressType.cryptonote, - subType: AddressSubType.receiving, - ); - - await mainDB.updateOrPutAddresses([newReceivingAddress]); - await info.updateReceivingAddress( - newAddress: newReceivingAddress.value, - isar: mainDB.isar, - ); - } - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Fatal); - } - await updateNode(); - - await cwWalletBase?.rescan(height: credentials.height); - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from recoverFromMnemonic(): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - }); - } - - @override - Future prepareSend({required TxData txData}) async { - try { - final feeRate = txData.feeRateType; - if (feeRate is FeeRateType) { - MoneroTransactionPriority feePriority; - switch (feeRate) { - case FeeRateType.fast: - feePriority = MoneroTransactionPriority.fast; - break; - case FeeRateType.average: - feePriority = MoneroTransactionPriority.regular; - break; - case FeeRateType.slow: - feePriority = MoneroTransactionPriority.slow; - break; - default: - throw ArgumentError("Invalid use of custom fee"); - } - - Future? awaitPendingTransaction; - try { - // check for send all - bool isSendAll = false; - final balance = await availableBalance; - if (txData.amount! == balance && - txData.recipients!.first.amount == balance) { - isSendAll = true; - } - - final List outputs = []; - for (final recipient in txData.recipients!) { - final output = wownero_output.Output(cwWalletBase!); - output.address = recipient.address; - output.sendAll = isSendAll; - final String amountToSend = recipient.amount.decimal.toString(); - output.setCryptoAmount(amountToSend); - outputs.add(output); - } - - final tmp = - wow_dart.wownero.createWowneroTransactionCreationCredentials( - outputs: outputs, - priority: feePriority, - ); - - final height = await chainHeight; - final inputs = txData.utxos - ?.map( - (e) => cw.UTXO( - address: e.address!, - hash: e.txid, - keyImage: e.keyImage!, - value: e.value, - isFrozen: e.isBlocked, - isUnlocked: e.blockHeight != null && - (height - (e.blockHeight ?? 0)) >= - cryptoCurrency.minConfirms, - height: e.blockHeight ?? 0, - vout: e.vout, - spent: e.used ?? false, - coinbase: e.isCoinbase, - ), - ) - .toList(); - - await prepareSendMutex.protect(() async { - awaitPendingTransaction = cwWalletBase!.createTransaction( - tmp, - inputs: inputs, - ); - }); - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from prepareSend(): $e\n$s", - level: LogLevel.Warning, - ); - } - - final PendingWowneroTransaction pendingWowneroTransaction = - await (awaitPendingTransaction!) as PendingWowneroTransaction; - final realFee = Amount.fromDecimal( - Decimal.parse(pendingWowneroTransaction.feeFormatted), - fractionDigits: cryptoCurrency.fractionDigits, - ); - - return txData.copyWith( - fee: realFee, - pendingWowneroTransaction: pendingWowneroTransaction, - ); - } else { - throw ArgumentError("Invalid fee rate argument provided!"); - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from prepare send(): $e\n$s", - level: LogLevel.Info, + Future getCreatedWallet({ + required String path, + required String password, + }) async => + await lib_monero.WowneroWallet.create( + path: path, + password: password, + seedType: lib_monero.WowneroSeedType + .fourteen, // TODO: check we want to actually use 14 here + overrideDeprecated14WordSeedException: true, ); - if (e.toString().contains("Incorrect unlocked balance")) { - throw Exception("Insufficient balance!"); - } else if (e is CreationTransactionException) { - throw Exception("Insufficient funds to pay for transaction fee!"); - } else { - throw Exception("Transaction failed with error code $e"); - } - } - } - @override - Future confirmSend({required TxData txData}) async { - try { - try { - await txData.pendingWowneroTransaction!.commit(); - Logging.instance.log( - "transaction ${txData.pendingWowneroTransaction!.id} has been sent", - level: LogLevel.Info, - ); - return txData.copyWith(txid: txData.pendingWowneroTransaction!.id); - } catch (e, s) { - Logging.instance.log( - "${info.name} wownero confirmSend: $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from confirmSend(): $e\n$s", - level: LogLevel.Info, + Future getRestoredWallet({ + required String path, + required String password, + required String mnemonic, + int height = 0, + }) async => + await lib_monero.WowneroWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, ); - rethrow; - } - } @override - Future get availableBalance async { - try { - int runningBalance = 0; - for (final entry in cwWalletBase!.balance!.entries) { - runningBalance += entry.value.unlockedBalance; - } - return Amount( - rawValue: BigInt.from(runningBalance), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } catch (_) { - return info.cachedBalance.spendable; + void invalidSeedLengthCheck(int length) { + if (!(length == 14 || length == 25)) { + throw Exception("Invalid wownero mnemonic length found: $length"); } } - - @override - Future get totalBalance async { - try { - final balanceEntries = cwWalletBase?.balance?.entries; - if (balanceEntries != null) { - int bal = 0; - for (final element in balanceEntries) { - bal = bal + element.value.fullBalance; - } - return Amount( - rawValue: BigInt.from(bal), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } else { - final transactions = cwWalletBase!.transactionHistory!.transactions; - int transactionBalance = 0; - for (final tx in transactions!.entries) { - if (tx.value.direction == TransactionDirection.incoming) { - transactionBalance += tx.value.amount!; - } else { - transactionBalance += -tx.value.amount! - tx.value.fee!; - } - } - - return Amount( - rawValue: BigInt.from(transactionBalance), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } - } catch (_) { - return info.cachedBalance.total; - } - } - - // ============== Private ==================================================== - - StreamSubscription? _torStatusListener; - StreamSubscription? _torPreferenceListener; - - final Mutex _torConnectingLock = Mutex(); - bool _requireMutex = false; } diff --git a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart new file mode 100644 index 000000000..5def92c41 --- /dev/null +++ b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart @@ -0,0 +1,1293 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_monero/cs_monero.dart' as lib_monero; +import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; +import 'package:stack_wallet_backup/generate_password.dart'; +import 'package:tuple/tuple.dart'; + +import '../../../db/hive/db.dart'; +import '../../../models/balance.dart'; +import '../../../models/isar/models/blockchain_data/address.dart'; +import '../../../models/isar/models/blockchain_data/transaction.dart'; +import '../../../models/isar/models/blockchain_data/utxo.dart'; +import '../../../models/keys/cw_key_data.dart'; +import '../../../models/paymint/fee_object_model.dart'; +import '../../../services/event_bus/events/global/blocks_remaining_event.dart'; +import '../../../services/event_bus/events/global/refresh_percent_changed_event.dart'; +import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; +import '../../../services/event_bus/events/global/updated_in_background_event.dart'; +import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; +import '../../../services/event_bus/global_event_bus.dart'; +import '../../../services/tor_service.dart'; +import '../../../utilities/amount/amount.dart'; +import '../../../utilities/enums/fee_rate_type_enum.dart'; +import '../../../utilities/logger.dart'; +import '../../../utilities/stack_file_system.dart'; +import '../../crypto_currency/intermediate/cryptonote_currency.dart'; +import '../../isar/models/wallet_info.dart'; +import '../../models/tx_data.dart'; +import '../wallet.dart'; +import '../wallet_mixin_interfaces/multi_address_interface.dart'; +import 'cryptonote_wallet.dart'; + +abstract class LibMoneroWallet + extends CryptonoteWallet implements MultiAddressInterface { + LibMoneroWallet(super.currency, this.compatType) { + final bus = GlobalEventBus.instance; + + // Listen for tor status changes. + _torStatusListener = bus.on().listen( + (event) async { + switch (event.newStatus) { + case TorConnectionStatus.connecting: + if (!_torConnectingLock.isLocked) { + await _torConnectingLock.acquire(); + } + _requireMutex = true; + break; + + case TorConnectionStatus.connected: + case TorConnectionStatus.disconnected: + if (_torConnectingLock.isLocked) { + _torConnectingLock.release(); + } + _requireMutex = false; + break; + } + }, + ); + + // Listen for tor preference changes. + _torPreferenceListener = bus.on().listen( + (event) async { + await updateNode(); + }, + ); + + // Potentially dangerous hack. See comments in _startInit() + _startInit(); + } + // cw based wallet listener to handle synchronization of utxo frozen states + late final StreamSubscription> _streamSub; + Future _startInit() async { + // Delay required as `mainDB` is not initialized in constructor. + // This is a hack and could lead to a race condition. + Future.delayed(const Duration(seconds: 2), () { + _streamSub = mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .watch(fireImmediately: true) + .listen((utxos) async { + await onUTXOsChanged(utxos); + await updateBalance(shouldUpdateUtxos: false); + }); + }); + } + + final lib_monero_compat.WalletType compatType; + lib_monero.Wallet? libMoneroWallet; + + lib_monero_compat.SyncStatus? get syncStatus => _syncStatus; + lib_monero_compat.SyncStatus? _syncStatus; + int _syncedCount = 0; + void _setSyncStatus(lib_monero_compat.SyncStatus status) { + if (status is lib_monero_compat.SyncedSyncStatus) { + if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { + _syncedCount++; + } + } else { + _syncedCount = 0; + } + + if (_syncedCount < 3) { + _syncStatus = status; + syncStatusChanged(); + } + } + + final prepareSendMutex = Mutex(); + final estimateFeeMutex = Mutex(); + + bool _txRefreshLock = false; + int _lastCheckedHeight = -1; + int _txCount = 0; + int currentKnownChainHeight = 0; + double highestPercentCached = 0; + + void loadWallet({required String path, required String password}); + + Future getCreatedWallet({ + required String path, + required String password, + }); + + Future getRestoredWallet({ + required String path, + required String password, + required String mnemonic, + int height = 0, + }); + + void invalidSeedLengthCheck(int length); + + bool walletExists(String path); + + void _setListener() { + libMoneroWallet?.addListener( + lib_monero.WalletListener( + onSyncingUpdate: onSyncingUpdate, + onNewBlock: onNewBlock, + onBalancesChanged: onBalancesChanged, + onError: (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Warning); + }, + ), + ); + } + + Future open() async { + bool wasNull = false; + + if (libMoneroWallet == null) { + wasNull = true; + // libMoneroWalletT?.close(); + final path = await pathForWallet( + name: walletId, + type: compatType, + ); + + final String password; + try { + password = (await secureStorageInterface.read( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + ))!; + } catch (e, s) { + throw Exception("Password not found $e, $s"); + } + + loadWallet(path: path, password: password); + + _setListener(); + + // TODO watcher + // libMoneroWallet?.syncStatusChanged = syncStatusChanged; + + await updateNode(); + } + + Address? currentAddress = await getCurrentReceivingAddress(); + if (currentAddress == null) { + currentAddress = addressFor(index: 0); + await mainDB.updateOrPutAddresses([currentAddress]); + } + if (info.cachedReceivingAddress != currentAddress.value) { + await info.updateReceivingAddress( + newAddress: currentAddress.value, + isar: mainDB.isar, + ); + } + + if (wasNull) { + try { + _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + libMoneroWallet?.startSyncing(); + } catch (_) { + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // TODO log + } + } + + libMoneroWallet?.startListeners(); + libMoneroWallet?.startAutoSaving(); + + unawaited(refresh()); + } + + @Deprecated("Only used in the case of older wallets") + lib_monero_compat.WalletInfo? getLibMoneroWalletInfo( + String walletId, + ) { + try { + return DB.instance.moneroWalletInfoBox.values.firstWhere( + (info) => info.id == lib_monero_compat.hiveIdFor(walletId, compatType), + ); + } catch (_) { + return null; + } + } + + Future save() async { + print("save is called"); + if (!Platform.isWindows) { + final appRoot = await StackFileSystem.applicationRootDirectory(); + await lib_monero_compat.backupWalletFiles( + name: walletId, + type: compatType, + appRoot: appRoot, + ); + } + await libMoneroWallet!.save(); + } + + Address addressFor({required int index, int account = 0}) { + final address = libMoneroWallet!.getAddress( + accountIndex: account, + addressIndex: index, + ); + + final newReceivingAddress = Address( + walletId: walletId, + derivationIndex: index, + derivationPath: null, + value: address.value, + publicKey: [], + type: AddressType.cryptonote, + subType: AddressSubType.receiving, + ); + + return newReceivingAddress; + } + + Future getKeys() async { + final base = libMoneroWallet; + + final oldInfo = getLibMoneroWalletInfo( + walletId, + ); + if (base == null || (oldInfo != null && oldInfo.name != walletId)) { + return null; + } + + return CWKeyData( + walletId: walletId, + publicViewKey: base.getPublicViewKey(), + privateViewKey: base.getPrivateViewKey(), + publicSpendKey: base.getPublicSpendKey(), + privateSpendKey: base.getPrivateSpendKey(), + ); + } + + @override + Future init({bool? isRestore}) async { + final path = await pathForWallet( + name: walletId, + type: compatType, + ); + if (!(walletExists(path)) && isRestore != true) { + try { + final password = generatePassword(); + await secureStorageInterface.write( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + value: password, + ); + final wallet = await getCreatedWallet(path: path, password: password); + + final height = wallet.getSyncFromBlockHeight(); + + await info.updateRestoreHeight( + newRestoreHeight: height, + isar: mainDB.isar, + ); + + // special case for xmr/wow. Normally mnemonic + passphrase is saved + // before wallet.init() is called + await secureStorageInterface.write( + key: Wallet.mnemonicKey(walletId: walletId), + value: wallet.getSeed().trim(), + ); + await secureStorageInterface.write( + key: Wallet.mnemonicPassphraseKey(walletId: walletId), + value: "", + ); + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Fatal); + } + await updateNode(); + } + + return super.init(); + } + + @override + Future recover({required bool isRescan}) async { + if (isRescan) { + await refreshMutex.protect(() async { + // clear blockchain info + await mainDB.deleteWalletBlockchainData(walletId); + + highestPercentCached = 0; + unawaited(libMoneroWallet?.rescanBlockchain()); + libMoneroWallet?.startSyncing(); + // unawaited(save()); + }); + unawaited(refresh()); + return; + } + + await refreshMutex.protect(() async { + final mnemonic = await getMnemonic(); + final seedLength = mnemonic.trim().split(" ").length; + + invalidSeedLengthCheck(seedLength); + + try { + final height = max(info.restoreHeight, 0); + + await info.updateRestoreHeight( + newRestoreHeight: height, + isar: mainDB.isar, + ); + + final String name = walletId; + + final path = await pathForWallet( + name: name, + type: compatType, + ); + + try { + final password = generatePassword(); + await secureStorageInterface.write( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + value: password, + ); + final wallet = await getRestoredWallet( + path: path, + password: password, + mnemonic: mnemonic, + height: height, + ); + + if (libMoneroWallet != null) { + await exit(); + } + + libMoneroWallet = wallet; + + _setListener(); + + final newReceivingAddress = await getCurrentReceivingAddress() ?? + Address( + walletId: walletId, + derivationIndex: 0, + derivationPath: null, + value: wallet.getAddress().value, + publicKey: [], + type: AddressType.cryptonote, + subType: AddressSubType.receiving, + ); + + await mainDB.updateOrPutAddresses([newReceivingAddress]); + await info.updateReceivingAddress( + newAddress: newReceivingAddress.value, + isar: mainDB.isar, + ); + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Fatal); + } + await updateNode(); + + // libMoneroWallet?.setRecoveringFromSeed(isRecovery: true); + unawaited(libMoneroWallet?.rescanBlockchain()); + libMoneroWallet?.startSyncing(); + + // await save(); + libMoneroWallet?.startListeners(); + libMoneroWallet?.startAutoSaving(); + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from recoverFromMnemonic(): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + }); + } + + @override + Future pingCheck() async { + return (await libMoneroWallet?.isConnectedToDaemon()) ?? false; + } + + @override + Future updateNode() async { + final node = getCurrentNode(); + + final host = Uri.parse(node.host).host; + ({InternetAddress host, int port})? proxy; + if (prefs.useTor) { + proxy = TorService.sharedInstance.getProxyInfo(); + } + + _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + try { + if (_requireMutex) { + await _torConnectingLock.protect(() async { + await libMoneroWallet?.connect( + daemonAddress: "$host:${node.port}", + daemonUsername: node.loginName, + daemonPassword: await node.getPassword(secureStorageInterface), + trusted: node.trusted ?? false, + useSSL: node.useSSL, + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", + ); + }); + } else { + await libMoneroWallet?.connect( + daemonAddress: "$host:${node.port}", + daemonUsername: node.loginName, + daemonPassword: await node.getPassword(secureStorageInterface), + trusted: node.trusted ?? false, + useSSL: node.useSSL, + socksProxyAddress: + proxy == null ? null : "${proxy.host.address}:${proxy.port}", + ); + } + + _setSyncStatus(lib_monero_compat.ConnectedSyncStatus()); + } catch (e, s) { + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + Logging.instance.log( + "Exception caught in $runtimeType.updateNode(): $e\n$s", + level: LogLevel.Error, + ); + } + + return; + } + + @override + Future updateTransactions() async { + final base = libMoneroWallet; + + if (base == null) { + return; + } + await base.refreshTransactions(); + final transactions = base.getTxs(); + + // final cachedTransactions = + // DB.instance.get(boxName: walletId, key: 'latest_tx_model') + // as TransactionData?; + // int latestTxnBlockHeight = + // DB.instance.get(boxName: walletId, key: "storedTxnDataHeight") + // as int? ?? + // 0; + // + // final txidsList = DB.instance + // .get(boxName: walletId, key: "cachedTxids") as List? ?? + // []; + // + // final Set cachedTxids = Set.from(txidsList); + + // TODO: filter to skip cached + confirmed txn processing in next step + // final unconfirmedCachedTransactions = + // cachedTransactions?.getAllTransactions() ?? {}; + // unconfirmedCachedTransactions + // .removeWhere((key, value) => value.confirmedStatus); + // + // if (cachedTransactions != null) { + // for (final tx in allTxHashes.toList(growable: false)) { + // final txHeight = tx["height"] as int; + // if (txHeight > 0 && + // txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) { + // if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) { + // allTxHashes.remove(tx); + // } + // } + // } + // } + + final List> txnsData = []; + + for (final tx in transactions) { + Address? address; + TransactionType type; + if (!tx.isSpend) { + final addressString = libMoneroWallet + ?.getAddress( + accountIndex: tx.accountIndex, + addressIndex: tx.addressIndex, + ) + .value; + + if (addressString != null) { + address = await mainDB + .getAddresses(walletId) + .filter() + .valueEqualTo(addressString) + .findFirst(); + } + + type = TransactionType.incoming; + } else { + // txn.address = ""; + type = TransactionType.outgoing; + } + + final txn = Transaction( + walletId: walletId, + txid: tx.hash, + timestamp: (tx.timeStamp.millisecondsSinceEpoch ~/ 1000), + type: type, + subType: TransactionSubType.none, + amount: tx.amount, + amountString: Amount( + rawValue: BigInt.from(tx.amount), + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), + fee: tx.fee, + height: tx.blockheight, + isCancelled: false, + isLelantus: false, + slateId: null, + otherData: null, + nonce: null, + inputs: [], + outputs: [], + numberOfMessages: null, + ); + + txnsData.add(Tuple2(txn, address)); + } + await mainDB.isar.writeTxn(() async { + await mainDB.isar.transactions + .where() + .walletIdEqualTo(walletId) + .deleteAll(); + for (final data in txnsData) { + final tx = data.item1; + + // save transaction + await mainDB.isar.transactions.put(tx); + + if (data.item2 != null) { + final address = await mainDB.getAddress(walletId, data.item2!.value); + + // check if address exists in db and add if it does not + if (address == null) { + await mainDB.isar.addresses.put(data.item2!); + } + + // link and save address + tx.address.value = address ?? data.item2!; + await tx.address.save(); + } + } + }); + } + + Future get availableBalance async { + try { + return Amount( + rawValue: BigInt.from(libMoneroWallet!.getUnlockedBalance()), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } catch (_) { + return info.cachedBalance.spendable; + } + } + + Future get totalBalance async { + try { + final full = libMoneroWallet?.getBalance(); + if (full != null) { + return Amount( + rawValue: BigInt.from(full), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } else { + final transactions = libMoneroWallet!.getTxs(); + int transactionBalance = 0; + for (final tx in transactions) { + if (!tx.isSpend) { + transactionBalance += tx.amount; + } else { + transactionBalance += -tx.amount - tx.fee; + } + } + + return Amount( + rawValue: BigInt.from(transactionBalance), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } + } catch (_) { + return info.cachedBalance.total; + } + } + + @override + Future exit() async { + libMoneroWallet?.stopAutoSaving(); + libMoneroWallet?.stopListeners(); + libMoneroWallet?.stopSyncing(); + await libMoneroWallet?.save(); + } + + Future pathForWalletDir({ + required String name, + required lib_monero_compat.WalletType type, + }) async { + final Directory root = await StackFileSystem.applicationRootDirectory(); + return lib_monero_compat.pathForWalletDir( + name: name, + type: type.name.toLowerCase(), + appRoot: root, + ); + } + + Future pathForWallet({ + required String name, + required lib_monero_compat.WalletType type, + }) async => + await pathForWalletDir(name: name, type: type) + .then((path) => '$path/$name'); + + void onSyncingUpdate({ + required int syncHeight, + required int nodeHeight, + String? message, + }) { + if (nodeHeight > 0 && syncHeight >= 0) { + currentKnownChainHeight = nodeHeight; + updateChainHeight(); + final blocksLeft = nodeHeight - syncHeight; + final lib_monero_compat.SyncStatus status; + if (blocksLeft < 100) { + status = lib_monero_compat.SyncedSyncStatus(); + + // if (!_hasSyncAfterStartup) { + // _hasSyncAfterStartup = true; + // await save(); + // } + // + // if (walletInfo.isRecovery!) { + // await setAsRecovered(); + // } + } else { + final percent = syncHeight / currentKnownChainHeight; + + status = lib_monero_compat.SyncingSyncStatus( + blocksLeft, + percent, + currentKnownChainHeight, + ); + } + + _setSyncStatus(status); + _refreshTxDataHelper(); + } + } + + void onBalancesChanged({ + required int newBalance, + required int newUnlockedBalance, + }) { + // do something? + } + + void onNewBlock(int nodeHeight) { + // do something? + } + + final _utxosUpdateLock = Mutex(); + Future onUTXOsChanged(List utxos) async { + await _utxosUpdateLock.protect(() async { + final cwUtxos = await libMoneroWallet?.getOutputs() ?? []; + + // bool changed = false; + + for (final cw in cwUtxos) { + final match = utxos.where( + (e) => + e.keyImage != null && + e.keyImage!.isNotEmpty && + e.keyImage == cw.keyImage, + ); + + if (match.isNotEmpty) { + final u = match.first; + + if (u.isBlocked) { + if (!cw.isFrozen) { + await libMoneroWallet?.freezeOutput(cw.keyImage); + // changed = true; + } + } else { + if (cw.isFrozen) { + await libMoneroWallet?.thawOutput(cw.keyImage); + // changed = true; + } + } + } + } + + // if (changed) { + // await libMoneroWallet?.updateUTXOs(); + // } + }); + } + + void onNewTransaction() { + // TODO: [prio=low] get rid of UpdatedInBackgroundEvent and move to + // adding the v2 tx to the db which would update ui automagically since the + // db is watched by the ui + // call this here? + GlobalEventBus.instance.fire( + UpdatedInBackgroundEvent( + "New data found in $walletId ${info.name} in background!", + walletId, + ), + ); + } + + void syncStatusChanged() async { + final _syncStatus = syncStatus; + + if (_syncStatus != null) { + if (_syncStatus.progress() == 1 && refreshMutex.isLocked) { + refreshMutex.release(); + } + + WalletSyncStatus? status; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); + + if (_syncStatus is lib_monero_compat.SyncingSyncStatus) { + final int blocksLeft = _syncStatus.blocksLeft; + + // ensure at least 1 to prevent math errors + final int height = max(1, _syncStatus.height); + + final nodeHeight = height + blocksLeft; + currentKnownChainHeight = nodeHeight; + + // final percent = height / nodeHeight; + final percent = _syncStatus.ptc; + + final highest = max(highestPercentCached, percent); + + final unchanged = highest == highestPercentCached; + if (unchanged) { + return; + } + + // update cached + if (highestPercentCached < percent) { + highestPercentCached = percent; + } + + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent( + highest, + walletId, + ), + ); + GlobalEventBus.instance.fire( + BlocksRemainingEvent( + blocksLeft, + walletId, + ), + ); + } else if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { + status = WalletSyncStatus.synced; + } else if (_syncStatus is lib_monero_compat.NotConnectedSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } else if (_syncStatus is lib_monero_compat.StartingSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent( + highestPercentCached, + walletId, + ), + ); + } else if (_syncStatus is lib_monero_compat.FailedSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } else if (_syncStatus is lib_monero_compat.ConnectingSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent( + highestPercentCached, + walletId, + ), + ); + } else if (_syncStatus is lib_monero_compat.ConnectedSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent( + highestPercentCached, + walletId, + ), + ); + } else if (_syncStatus is lib_monero_compat.LostConnectionSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } + + if (status != null) { + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + status, + walletId, + info.coin, + ), + ); + } + } + } + + @override + Future checkSaveInitialReceivingAddress() async { + // this doesn't work without opening the wallet first which takes a while + } + + // ============ Private ====================================================== + Future _refreshTxDataHelper() async { + if (_txRefreshLock) return; + _txRefreshLock = true; + + final _syncStatus = syncStatus; + + if (_syncStatus != null && + _syncStatus is lib_monero_compat.SyncingSyncStatus) { + final int blocksLeft = _syncStatus.blocksLeft; + final tenKChange = blocksLeft ~/ 10000; + + // only refresh transactions periodically during a sync + if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { + _lastCheckedHeight = tenKChange; + await _refreshTxData(); + } + } else { + await _refreshTxData(); + } + + _txRefreshLock = false; + } + + Future _refreshTxData() async { + await updateTransactions(); + final count = await mainDB.getTransactions(walletId).count(); + + if (count > _txCount) { + _txCount = count; + await updateBalance(); + GlobalEventBus.instance.fire( + UpdatedInBackgroundEvent( + "New transaction data found in $walletId ${info.name}!", + walletId, + ), + ); + } + } + + // ============ Overrides ==================================================== + + @override + FilterOperation? get changeAddressFilterOperation => null; + + @override + FilterOperation? get receivingAddressFilterOperation => null; + + @override + Future updateUTXOs() async { + final List outputArray = []; + final utxos = await libMoneroWallet?.getOutputs() ?? []; + for (final rawUTXO in utxos) { + if (!rawUTXO.spent) { + final current = await mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .voutEqualTo(rawUTXO.vout) + .and() + .txidEqualTo(rawUTXO.hash) + .findFirst(); + final tx = await mainDB.isar.transactions + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(rawUTXO.hash) + .findFirst(); + + final otherDataMap = { + "keyImage": rawUTXO.keyImage, + "spent": rawUTXO.spent, + }; + + final utxo = UTXO( + address: rawUTXO.address, + walletId: walletId, + txid: rawUTXO.hash, + vout: rawUTXO.vout, + value: rawUTXO.value, + name: current?.name ?? "", + isBlocked: current?.isBlocked ?? rawUTXO.isFrozen, + blockedReason: current?.blockedReason ?? "", + isCoinbase: rawUTXO.coinbase, + blockHash: "", + blockHeight: + tx?.height ?? (rawUTXO.height > 0 ? rawUTXO.height : null), + blockTime: tx?.timestamp, + otherData: jsonEncode(otherDataMap), + ); + + outputArray.add(utxo); + } + } + + await mainDB.updateUTXOs(walletId, outputArray); + + return true; + } + + @override + Future updateBalance({bool shouldUpdateUtxos = true}) async { + if (shouldUpdateUtxos) { + await updateUTXOs(); + } + + final total = await totalBalance; + final available = await availableBalance; + + final balance = Balance( + total: total, + spendable: available, + blockedTotal: Amount( + rawValue: BigInt.zero, + fractionDigits: cryptoCurrency.fractionDigits, + ), + pendingSpendable: total - available, + ); + + await info.updateBalance(newBalance: balance, isar: mainDB.isar); + } + + @override + Future refresh() async { + // Awaiting this lock could be dangerous. + // Since refresh is periodic (generally) + if (refreshMutex.isLocked) { + return; + } + + // this acquire should be almost instant due to above check. + // Slight possibility of race but should be irrelevant + await refreshMutex.acquire(); + + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.syncing, + walletId, + info.coin, + ), + ); + + await updateTransactions(); + await updateBalance(); + + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + await checkReceivingAddressForTransactions(); + } + + if (syncStatus is lib_monero_compat.SyncedSyncStatus) { + // refreshMutex.release(); + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.synced, + walletId, + info.coin, + ), + ); + } + } + + @override + Future generateNewReceivingAddress() async { + try { + final currentReceiving = await getCurrentReceivingAddress(); + + final newReceivingIndex = + currentReceiving == null ? 0 : currentReceiving.derivationIndex + 1; + + final newReceivingAddress = addressFor(index: newReceivingIndex); + + // Add that new receiving address + await mainDB.putAddress(newReceivingAddress); + await info.updateReceivingAddress( + newAddress: newReceivingAddress.value, + isar: mainDB.isar, + ); + } catch (e, s) { + Logging.instance.log( + "Exception in generateNewAddress(): $e\n$s", + level: LogLevel.Error, + ); + } + } + + @override + Future checkReceivingAddressForTransactions() async { + if (info.otherData[WalletInfoKeys.reuseAddress] == true) { + try { + throw Exception(); + } catch (_, s) { + Logging.instance.log( + "checkReceivingAddressForTransactions called but reuse address flag set: $s", + level: LogLevel.Error, + ); + } + } + + try { + int highestIndex = -1; + final entries = libMoneroWallet?.getTxs(); + if (entries != null) { + for (final element in entries) { + if (!element.isSpend) { + final int curAddressIndex = element.addressIndex; + if (curAddressIndex > highestIndex) { + highestIndex = curAddressIndex; + } + } + } + } + + // Check the new receiving index + final currentReceiving = await getCurrentReceivingAddress(); + final curIndex = currentReceiving?.derivationIndex ?? -1; + + if (highestIndex >= curIndex) { + // First increment the receiving index + final newReceivingIndex = curIndex + 1; + + // Use new index to derive a new receiving address + final newReceivingAddress = addressFor(index: newReceivingIndex); + + final existing = await mainDB + .getAddresses(walletId) + .filter() + .valueEqualTo(newReceivingAddress.value) + .findFirst(); + if (existing == null) { + // Add that new change address + await mainDB.putAddress(newReceivingAddress); + } else { + // we need to update the address + await mainDB.updateAddress(existing, newReceivingAddress); + } + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + // keep checking until address with no tx history is set as current + await checkReceivingAddressForTransactions(); + } + } + } on SocketException catch (se, s) { + Logging.instance.log( + "SocketException caught in _checkReceivingAddressForTransactions(): $se\n$s", + level: LogLevel.Error, + ); + return; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from _checkReceivingAddressForTransactions(): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } + + // TODO: this needs some work. Prio's may need to be changed as well as estimated blocks + @override + Future get fees async => FeeObject( + numberOfBlocksFast: 10, + numberOfBlocksAverage: 15, + numberOfBlocksSlow: 20, + fast: lib_monero.TransactionPriority.high.value, + medium: lib_monero.TransactionPriority.medium.value, + slow: lib_monero.TransactionPriority.normal.value, + ); + + @override + Future updateChainHeight() async { + await info.updateCachedChainHeight( + newHeight: currentKnownChainHeight, + isar: mainDB.isar, + ); + } + + @override + Future checkChangeAddressForTransactions() async { + // do nothing + } + + @override + Future generateNewChangeAddress() async { + // do nothing + } + + @override + Future prepareSend({required TxData txData}) async { + try { + final feeRate = txData.feeRateType; + if (feeRate is FeeRateType) { + lib_monero.TransactionPriority feePriority; + switch (feeRate) { + case FeeRateType.fast: + feePriority = lib_monero.TransactionPriority.high; + break; + case FeeRateType.average: + feePriority = lib_monero.TransactionPriority.medium; + break; + case FeeRateType.slow: + feePriority = lib_monero.TransactionPriority.normal; + break; + default: + throw ArgumentError("Invalid use of custom fee"); + } + + try { + // TODO: ??? + // check for send all + bool isSendAll = false; + + final balance = await availableBalance; + if (txData.amount! == balance && + txData.recipients!.first.amount == balance) { + isSendAll = true; + } + + if (isSendAll && txData.recipients!.length > 1) { + throw Exception("Send all not supported with multiple recipients"); + } + + final List outputs = []; + for (final recipient in txData.recipients!) { + final output = lib_monero.Recipient( + address: recipient.address, + amount: recipient.amount.decimal.toString(), + ); + + outputs.add(output); + } + + if (outputs.isEmpty) { + throw Exception("No recipients provided"); + } + + final height = await chainHeight; + final inputs = txData.utxos + ?.map( + (e) => lib_monero.Output( + address: e.address!, + hash: e.txid, + keyImage: e.keyImage!, + value: e.value, + isFrozen: e.isBlocked, + isUnlocked: e.blockHeight != null && + (height - (e.blockHeight ?? 0)) >= + cryptoCurrency.minConfirms, + height: e.blockHeight ?? 0, + vout: e.vout, + spent: e.used ?? false, + coinbase: e.isCoinbase, + ), + ) + .toList(); + + return await prepareSendMutex.protect(() async { + final lib_monero.PendingTransaction pendingTransaction; + if (outputs.length == 1) { + pendingTransaction = await libMoneroWallet!.createTx( + address: outputs.first.address, + paymentId: "", + amount: isSendAll ? null : outputs.first.amount, + priority: feePriority, + preferredInputs: inputs?.map((e) => e.keyImage).toList() ?? [], + ); + } else { + pendingTransaction = await libMoneroWallet!.createTxMultiDest( + outputs: outputs, + paymentId: "", + priority: feePriority, + preferredInputs: inputs?.map((e) => e.keyImage).toList() ?? [], + ); + } + + final realFee = Amount( + rawValue: BigInt.from(pendingTransaction.fee!), + fractionDigits: cryptoCurrency.fractionDigits, + ); + + return txData.copyWith( + fee: realFee, + pendingTransaction: pendingTransaction, + ); + }); + } catch (e) { + rethrow; + } + } else { + throw ArgumentError("Invalid fee rate argument provided!"); + } + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from prepare send(): $e\n$s", + level: LogLevel.Info, + ); + + if (e.toString().contains("Incorrect unlocked balance")) { + throw Exception("Insufficient balance!"); + } else if (e is lib_monero.CreationTransactionException) { + throw Exception("Insufficient funds to pay for transaction fee!"); + } else { + throw Exception("Transaction failed with error: $e"); + } + } + } + + @override + Future confirmSend({required TxData txData}) async { + try { + try { + await libMoneroWallet!.commitTx( + txData.pendingTransaction!, + ); + + Logging.instance.log( + "transaction ${txData.pendingTransaction!.txid} has been sent", + level: LogLevel.Info, + ); + return txData.copyWith(txid: txData.pendingTransaction!.txid); + } catch (e, s) { + Logging.instance.log( + "${info.name} ${compatType.name.toLowerCase()} confirmSend: $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from confirmSend(): $e\n$s", + level: LogLevel.Info, + ); + rethrow; + } + } + + // ============== Private ==================================================== + + StreamSubscription? _torStatusListener; + StreamSubscription? _torPreferenceListener; + + final Mutex _torConnectingLock = Mutex(); + bool _requireMutex = false; +} diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart deleted file mode 100644 index ba054fa1f..000000000 --- a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart +++ /dev/null @@ -1,532 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:math'; - -import 'package:cw_core/monero_transaction_priority.dart'; -import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/transaction_direction.dart'; -import 'package:cw_core/utxo.dart' as cw; -import 'package:cw_core/wallet_base.dart'; -import 'package:cw_core/wallet_service.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:flutter_libmonero/core/key_service.dart'; -import 'package:isar/isar.dart'; -import 'package:mutex/mutex.dart'; - -import '../../../models/balance.dart'; -import '../../../models/isar/models/blockchain_data/address.dart'; -import '../../../models/isar/models/blockchain_data/transaction.dart'; -import '../../../models/isar/models/blockchain_data/utxo.dart'; -import '../../../models/keys/cw_key_data.dart'; -import '../../../models/paymint/fee_object_model.dart'; -import '../../../services/event_bus/events/global/blocks_remaining_event.dart'; -import '../../../services/event_bus/events/global/refresh_percent_changed_event.dart'; -import '../../../services/event_bus/events/global/updated_in_background_event.dart'; -import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; -import '../../../services/event_bus/global_event_bus.dart'; -import '../../../utilities/amount/amount.dart'; -import '../../../utilities/logger.dart'; -import '../../../utilities/stack_file_system.dart'; -import '../../crypto_currency/intermediate/cryptonote_currency.dart'; -import '../../isar/models/wallet_info.dart'; -import '../intermediate/cryptonote_wallet.dart'; -import 'multi_address_interface.dart'; - -mixin CwBasedInterface on CryptonoteWallet - implements MultiAddressInterface { - final prepareSendMutex = Mutex(); - final estimateFeeMutex = Mutex(); - - KeyService? _cwKeysStorageCached; - KeyService get cwKeysStorage => - _cwKeysStorageCached ??= KeyService(secureStorageInterface); - - bool _txRefreshLock = false; - int _lastCheckedHeight = -1; - int _txCount = 0; - int currentKnownChainHeight = 0; - double highestPercentCached = 0; - - Timer? autoSaveTimer; - Future pathForWalletDir({ - required String name, - required WalletType type, - }) async { - final Directory root = await StackFileSystem.applicationRootDirectory(); - - final prefix = walletTypeToString(type).toLowerCase(); - final walletsDir = Directory('${root.path}/wallets'); - final walletDire = Directory('${walletsDir.path}/$prefix/$name'); - - if (!walletDire.existsSync()) { - walletDire.createSync(recursive: true); - } - - return walletDire.path; - } - - Future pathForWallet({ - required String name, - required WalletType type, - }) async => - await pathForWalletDir(name: name, type: type) - .then((path) => '$path/$name'); - - void onNewBlock({required int height, required int blocksLeft}) { - currentKnownChainHeight = height; - updateChainHeight(); - _refreshTxDataHelper(); - } - - final _utxosUpdateLock = Mutex(); - Future onUTXOsCHanged(List utxos) async { - await _utxosUpdateLock.protect(() async { - final cwUtxos = cwWalletBase?.utxos ?? []; - - bool changed = false; - - for (final cw in cwUtxos) { - final match = utxos.where( - (e) => - e.keyImage != null && - e.keyImage!.isNotEmpty && - e.keyImage == cw.keyImage, - ); - - if (match.isNotEmpty) { - final u = match.first; - - if (u.isBlocked) { - if (!cw.isFrozen) { - await cwWalletBase?.freeze(cw.keyImage); - changed = true; - } - } else { - if (cw.isFrozen) { - await cwWalletBase?.thaw(cw.keyImage); - changed = true; - } - } - } - } - - if (changed) { - await cwWalletBase?.updateUTXOs(); - } - }); - } - - void onNewTransaction() { - // TODO: [prio=low] get rid of UpdatedInBackgroundEvent and move to - // adding the v2 tx to the db which would update ui automagically since the - // db is watched by the ui - // call this here? - GlobalEventBus.instance.fire( - UpdatedInBackgroundEvent( - "New data found in $walletId ${info.name} in background!", - walletId, - ), - ); - } - - void syncStatusChanged() async { - final syncStatus = cwWalletBase?.syncStatus; - - if (syncStatus != null) { - if (syncStatus.progress() == 1 && refreshMutex.isLocked) { - refreshMutex.release(); - } - - WalletSyncStatus? status; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); - - if (syncStatus is SyncingSyncStatus) { - final int blocksLeft = syncStatus.blocksLeft; - - // ensure at least 1 to prevent math errors - final int height = max(1, syncStatus.height); - - final nodeHeight = height + blocksLeft; - currentKnownChainHeight = nodeHeight; - - final percent = height / nodeHeight; - - final highest = max(highestPercentCached, percent); - - // update cached - if (highestPercentCached < percent) { - highestPercentCached = percent; - } - - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent( - highest, - walletId, - ), - ); - GlobalEventBus.instance.fire( - BlocksRemainingEvent( - blocksLeft, - walletId, - ), - ); - } else if (syncStatus is SyncedSyncStatus) { - status = WalletSyncStatus.synced; - } else if (syncStatus is NotConnectedSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } else if (syncStatus is StartingSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent( - highestPercentCached, - walletId, - ), - ); - } else if (syncStatus is FailedSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } else if (syncStatus is ConnectingSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent( - highestPercentCached, - walletId, - ), - ); - } else if (syncStatus is ConnectedSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent( - highestPercentCached, - walletId, - ), - ); - } else if (syncStatus is LostConnectionSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } - - if (status != null) { - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent( - status, - walletId, - info.coin, - ), - ); - } - } - } - - @override - Future checkSaveInitialReceivingAddress() async { - // this doesn't work without opening the wallet first which takes a while - } - - // ============ Interface ==================================================== - - U? get cwWalletBase; - V? get cwWalletService; - - Future get availableBalance; - Future get totalBalance; - - Future open(); - - Address addressFor({required int index, int account = 0}); - - Future getKeys(); - - // ============ Private ====================================================== - Future _refreshTxDataHelper() async { - if (_txRefreshLock) return; - _txRefreshLock = true; - - final syncStatus = cwWalletBase?.syncStatus; - - if (syncStatus != null && syncStatus is SyncingSyncStatus) { - final int blocksLeft = syncStatus.blocksLeft; - final tenKChange = blocksLeft ~/ 10000; - - // only refresh transactions periodically during a sync - if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { - _lastCheckedHeight = tenKChange; - await _refreshTxData(); - } - } else { - await _refreshTxData(); - } - - _txRefreshLock = false; - } - - Future _refreshTxData() async { - await updateTransactions(); - final count = await mainDB.getTransactions(walletId).count(); - - if (count > _txCount) { - _txCount = count; - await updateBalance(); - GlobalEventBus.instance.fire( - UpdatedInBackgroundEvent( - "New transaction data found in $walletId ${info.name}!", - walletId, - ), - ); - } - } - - // ============ Overrides ==================================================== - - @override - FilterOperation? get changeAddressFilterOperation => null; - - @override - FilterOperation? get receivingAddressFilterOperation => null; - - @override - Future updateUTXOs() async { - await cwWalletBase?.updateUTXOs(); - - final List outputArray = []; - for (final rawUTXO in (cwWalletBase?.utxos ?? [])) { - if (!rawUTXO.spent) { - final current = await mainDB.isar.utxos - .where() - .walletIdEqualTo(walletId) - .filter() - .voutEqualTo(rawUTXO.vout) - .and() - .txidEqualTo(rawUTXO.hash) - .findFirst(); - final tx = await mainDB.isar.transactions - .where() - .walletIdEqualTo(walletId) - .filter() - .txidEqualTo(rawUTXO.hash) - .findFirst(); - - final otherDataMap = { - "keyImage": rawUTXO.keyImage, - "spent": rawUTXO.spent, - }; - - final utxo = UTXO( - address: rawUTXO.address, - walletId: walletId, - txid: rawUTXO.hash, - vout: rawUTXO.vout, - value: rawUTXO.value, - name: current?.name ?? "", - isBlocked: current?.isBlocked ?? rawUTXO.isFrozen, - blockedReason: current?.blockedReason ?? "", - isCoinbase: rawUTXO.coinbase, - blockHash: "", - blockHeight: - tx?.height ?? (rawUTXO.height > 0 ? rawUTXO.height : null), - blockTime: tx?.timestamp, - otherData: jsonEncode(otherDataMap), - ); - - outputArray.add(utxo); - } - } - - await mainDB.updateUTXOs(walletId, outputArray); - - return true; - } - - @override - Future updateBalance({bool shouldUpdateUtxos = true}) async { - if (shouldUpdateUtxos) { - await updateUTXOs(); - } - - final total = await totalBalance; - final available = await availableBalance; - - final balance = Balance( - total: total, - spendable: available, - blockedTotal: Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ), - pendingSpendable: total - available, - ); - - await info.updateBalance(newBalance: balance, isar: mainDB.isar); - } - - @override - Future refresh() async { - // Awaiting this lock could be dangerous. - // Since refresh is periodic (generally) - if (refreshMutex.isLocked) { - return; - } - - // this acquire should be almost instant due to above check. - // Slight possibility of race but should be irrelevant - await refreshMutex.acquire(); - - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent( - WalletSyncStatus.syncing, - walletId, - info.coin, - ), - ); - - await updateTransactions(); - await updateBalance(); - - if (info.otherData[WalletInfoKeys.reuseAddress] != true) { - await checkReceivingAddressForTransactions(); - } - - if (cwWalletBase?.syncStatus is SyncedSyncStatus) { - refreshMutex.release(); - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent( - WalletSyncStatus.synced, - walletId, - info.coin, - ), - ); - } - } - - @override - Future exit() async { - autoSaveTimer?.cancel(); - await cwWalletBase?.save(); - cwWalletBase?.close(); - } - - @override - Future generateNewReceivingAddress() async { - try { - final currentReceiving = await getCurrentReceivingAddress(); - - final newReceivingIndex = - currentReceiving == null ? 0 : currentReceiving.derivationIndex + 1; - - final newReceivingAddress = addressFor(index: newReceivingIndex); - - // Add that new receiving address - await mainDB.putAddress(newReceivingAddress); - await info.updateReceivingAddress( - newAddress: newReceivingAddress.value, - isar: mainDB.isar, - ); - } catch (e, s) { - Logging.instance.log( - "Exception in generateNewAddress(): $e\n$s", - level: LogLevel.Error, - ); - } - } - - @override - Future checkReceivingAddressForTransactions() async { - if (info.otherData[WalletInfoKeys.reuseAddress] == true) { - try { - throw Exception(); - } catch (_, s) { - Logging.instance.log( - "checkReceivingAddressForTransactions called but reuse address flag set: $s", - level: LogLevel.Error, - ); - } - } - - try { - int highestIndex = -1; - final entries = cwWalletBase?.transactionHistory?.transactions?.entries; - if (entries != null) { - for (final element in entries) { - if (element.value.direction == TransactionDirection.incoming) { - final int curAddressIndex = - element.value.additionalInfo!['addressIndex'] as int; - if (curAddressIndex > highestIndex) { - highestIndex = curAddressIndex; - } - } - } - } - - // Check the new receiving index - final currentReceiving = await getCurrentReceivingAddress(); - final curIndex = currentReceiving?.derivationIndex ?? -1; - - if (highestIndex >= curIndex) { - // First increment the receiving index - final newReceivingIndex = curIndex + 1; - - // Use new index to derive a new receiving address - final newReceivingAddress = addressFor(index: newReceivingIndex); - - final existing = await mainDB - .getAddresses(walletId) - .filter() - .valueEqualTo(newReceivingAddress.value) - .findFirst(); - if (existing == null) { - // Add that new change address - await mainDB.putAddress(newReceivingAddress); - } else { - // we need to update the address - await mainDB.updateAddress(existing, newReceivingAddress); - } - if (info.otherData[WalletInfoKeys.reuseAddress] != true) { - // keep checking until address with no tx history is set as current - await checkReceivingAddressForTransactions(); - } - } - } on SocketException catch (se, s) { - Logging.instance.log( - "SocketException caught in _checkReceivingAddressForTransactions(): $se\n$s", - level: LogLevel.Error, - ); - return; - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from _checkReceivingAddressForTransactions(): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } - - @override - Future get fees async => FeeObject( - numberOfBlocksFast: 10, - numberOfBlocksAverage: 15, - numberOfBlocksSlow: 20, - fast: MoneroTransactionPriority.fast.raw!, - medium: MoneroTransactionPriority.regular.raw!, - slow: MoneroTransactionPriority.slow.raw!, - ); - @override - Future updateChainHeight() async { - await info.updateCachedChainHeight( - newHeight: currentKnownChainHeight, - isar: mainDB.isar, - ); - } - - @override - Future checkChangeAddressForTransactions() async { - // do nothing - } - - @override - Future generateNewChangeAddress() async { - // do nothing - } -} diff --git a/lib/widgets/desktop/desktop_fee_dialog.dart b/lib/widgets/desktop/desktop_fee_dialog.dart index 90304e80c..322fbb876 100644 --- a/lib/widgets/desktop/desktop_fee_dialog.dart +++ b/lib/widgets/desktop/desktop_fee_dialog.dart @@ -1,6 +1,7 @@ -import 'package:cw_core/monero_transaction_priority.dart'; import 'package:flutter/material.dart'; +import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../models/models.dart'; import '../../pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart'; import '../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart'; @@ -61,7 +62,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.fast.raw!, + lib_monero.TransactionPriority.high.value, ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { @@ -111,7 +112,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.regular.raw!, + lib_monero.TransactionPriority.medium.value, ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { @@ -161,7 +162,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - MoneroTransactionPriority.slow.raw!, + lib_monero.TransactionPriority.normal.value, ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { diff --git a/lib/widgets/wallet_card.dart b/lib/widgets/wallet_card.dart index a2185186c..79ab0d41d 100644 --- a/lib/widgets/wallet_card.dart +++ b/lib/widgets/wallet_card.dart @@ -27,8 +27,8 @@ import '../utilities/util.dart'; import '../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../wallets/wallet/impl/ethereum_wallet.dart'; import '../wallets/wallet/impl/sub_wallets/eth_token_wallet.dart'; +import '../wallets/wallet/intermediate/lib_monero_wallet.dart'; import '../wallets/wallet/wallet.dart'; -import '../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart'; import 'conditional_parent.dart'; import 'desktop/primary_button.dart'; import 'dialogs/basic_dialog.dart'; @@ -97,7 +97,7 @@ class SimpleWalletCard extends ConsumerWidget { if (context.mounted) { final Future loadFuture; - if (wallet is CwBasedInterface) { + if (wallet is LibMoneroWallet) { loadFuture = wallet.init().then((value) async => await (wallet).open()); } else { loadFuture = wallet.init(); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 499e835f2..df4b140d1 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,10 +6,10 @@ #include "generated_plugin_registrant.h" +#include #include #include #include -#include #include #include #include @@ -18,6 +18,9 @@ #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) cs_monero_flutter_libs_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "CsMoneroFlutterLibsPlugin"); + cs_monero_flutter_libs_plugin_register_with_registrar(cs_monero_flutter_libs_registrar); g_autoptr(FlPluginRegistrar) desktop_drop_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); @@ -27,9 +30,6 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) flutter_libepiccash_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibepiccashPlugin"); flutter_libepiccash_plugin_register_with_registrar(flutter_libepiccash_registrar); - g_autoptr(FlPluginRegistrar) flutter_libmonero_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibmoneroPlugin"); - flutter_libmonero_plugin_register_with_registrar(flutter_libmonero_registrar); g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index b4a4986e6..f92bb010e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,10 +3,10 @@ # list(APPEND FLUTTER_PLUGIN_LIST + cs_monero_flutter_libs desktop_drop devicelocale flutter_libepiccash - flutter_libmonero flutter_secure_storage_linux isar_flutter_libs sqlite3_flutter_libs diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a8e802cea..5886d38ca 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,6 +7,7 @@ import Foundation import camera_macos import connectivity_plus +import cs_monero_flutter_libs import desktop_drop import device_info_plus import devicelocale @@ -28,6 +29,7 @@ import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { CameraMacosPlugin.register(with: registry.registrar(forPlugin: "CameraMacosPlugin")) ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + CsMoneroFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "CsMoneroFlutterLibsPlugin")) DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) diff --git a/pubspec.lock b/pubspec.lock index 48bab8292..d436cea3d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -54,14 +54,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.0" - asn1lib: - dependency: transitive - description: - name: asn1lib - sha256: "2ca377ad4d677bbadca278e0ba4da4e057b80a10b927bfc8f7d8bda8fe2ceb75" - url: "https://pub.dev" - source: hosted - version: "1.5.4" async: dependency: "direct main" description: @@ -374,6 +366,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + compat: + dependency: "direct main" + description: + path: "crypto_plugins/cs_monero/compat" + relative: true + source: path + version: "1.0.0" connectivity_plus: dependency: "direct main" description: @@ -430,6 +429,20 @@ packages: url: "https://pub.dev" source: hosted version: "2.7.0" + cs_monero: + dependency: "direct main" + description: + path: "crypto_plugins/cs_monero/cs_monero" + relative: true + source: path + version: "1.0.0" + cs_monero_flutter_libs: + dependency: "direct main" + description: + path: "crypto_plugins/cs_monero/cs_monero_flutter_libs" + relative: true + source: path + version: "1.0.0" csslib: dependency: transitive description: @@ -438,27 +451,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - cw_core: - dependency: "direct main" - description: - path: "crypto_plugins/flutter_libmonero/cw_core" - relative: true - source: path - version: "0.0.1" - cw_monero: - dependency: "direct main" - description: - path: "crypto_plugins/flutter_libmonero/cw_monero" - relative: true - source: path - version: "0.0.1" - cw_wownero: - dependency: "direct main" - description: - path: "crypto_plugins/flutter_libmonero/cw_wownero" - relative: true - source: path - version: "0.0.1" dart_base_x: dependency: transitive description: @@ -628,14 +620,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.9" - encrypt: - dependency: transitive - description: - name: encrypt - sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" - url: "https://pub.dev" - source: hosted - version: "5.0.3" equatable: dependency: "direct main" description: @@ -750,13 +734,6 @@ packages: relative: true source: path version: "0.0.1" - flutter_libmonero: - dependency: "direct main" - description: - path: "crypto_plugins/flutter_libmonero" - relative: true - source: path - version: "0.0.1" flutter_libsparkmobile: dependency: "direct main" description: @@ -798,14 +775,6 @@ packages: url: "https://pub.dev" source: hosted version: "7.2.0" - flutter_mobx: - dependency: transitive - description: - name: flutter_mobx - sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" - url: "https://pub.dev" - source: hosted - version: "2.2.1+1" flutter_native_splash: dependency: "direct main" description: @@ -1217,6 +1186,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.11" + logger: + dependency: transitive + description: + name: logger + sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32" + url: "https://pub.dev" + source: hosted + version: "2.4.0" logging: dependency: transitive description: @@ -1281,14 +1258,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" - mobx: - dependency: transitive - description: - name: mobx - sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" - url: "https://pub.dev" - source: hosted - version: "2.3.3+2" mockingjay: dependency: "direct dev" description: @@ -1338,14 +1307,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" nm: dependency: transitive description: @@ -1578,14 +1539,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0" - provider: - dependency: transitive - description: - name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://pub.dev" - source: hosted - version: "6.1.2" pub_semver: dependency: transitive description: diff --git a/scripts/android/build_all.sh b/scripts/android/build_all.sh index 7cb2e083d..e9417b263 100755 --- a/scripts/android/build_all.sh +++ b/scripts/android/build_all.sh @@ -14,7 +14,7 @@ PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) (cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/cs_monero/tools/build_scripts && ./build_android.sh ) set_rust_to_1720 (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) diff --git a/scripts/android/build_all_campfire.sh b/scripts/android/build_all_campfire.sh index 7cb2e083d..e9417b263 100755 --- a/scripts/android/build_all_campfire.sh +++ b/scripts/android/build_all_campfire.sh @@ -14,7 +14,7 @@ PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) (cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/cs_monero/tools/build_scripts && ./build_android.sh ) set_rust_to_1720 (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) diff --git a/scripts/android/build_all_duo.sh b/scripts/android/build_all_duo.sh index aec2ebbb3..f184a71dd 100755 --- a/scripts/android/build_all_duo.sh +++ b/scripts/android/build_all_duo.sh @@ -16,7 +16,7 @@ PLUGINS_DIR=../../crypto_plugins (cd "${PLUGINS_DIR}"/flutter_liblelantus/scripts/android && ./build_all.sh ) (cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmonero/scripts/android/ && ./build_all.sh ) +(cd "${PLUGINS_DIR}"/cs_monero/tools/build_scripts && ./build_android.sh ) set_rust_to_1720 (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) diff --git a/scripts/app_config/templates/android/app/build.gradle b/scripts/app_config/templates/android/app/build.gradle index 631c1300e..60e622ec9 100644 --- a/scripts/app_config/templates/android/app/build.gradle +++ b/scripts/app_config/templates/android/app/build.gradle @@ -45,14 +45,14 @@ android { abiFilters "x86_64","armeabi-v7a", "arm64-v8a" } - externalNativeBuild { - cmake { - arguments "-DANDROID_STL=c++_shared", '-DBUILD_TESTING=OFF', "-DANDROID_TOOLCHAIN=clang -v" - cppFlags "-frtti -fexceptions -v -DANDROID -std=c++17" -// cppFlags "-std=c++11" - version "3.10.2" - } - } +// externalNativeBuild { +// cmake { +// arguments "-DANDROID_STL=c++_shared", '-DBUILD_TESTING=OFF', "-DANDROID_TOOLCHAIN=clang -v" +// cppFlags "-frtti -fexceptions -v -DANDROID -std=c++17" +//// cppFlags "-std=c++11" +// version "3.10.2" +// } +// } } signingConfigs { diff --git a/scripts/app_config/templates/linux/CMakeLists.txt b/scripts/app_config/templates/linux/CMakeLists.txt index a13bec888..25750ef4f 100644 --- a/scripts/app_config/templates/linux/CMakeLists.txt +++ b/scripts/app_config/templates/linux/CMakeLists.txt @@ -134,12 +134,6 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR} install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "monero_libwallet2_api_c.so" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "wownero_libwallet2_api_c.so" - COMPONENT Runtime) - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index b0dc23bc4..5626025ac 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -37,17 +37,14 @@ dependencies: url: https://github.com/cypherstack/flutter_libsparkmobile.git ref: cc7b43b731e4a7906dd25d4364a08e34554cee19 - flutter_libmonero: - path: ./crypto_plugins/flutter_libmonero + cs_monero: + path: ./crypto_plugins/cs_monero/cs_monero - cw_monero: - path: ./crypto_plugins/flutter_libmonero/cw_monero + compat: + path: ./crypto_plugins/cs_monero/compat - cw_wownero: - path: ./crypto_plugins/flutter_libmonero/cw_wownero - - cw_core: - path: ./crypto_plugins/flutter_libmonero/cw_core + cs_monero_flutter_libs: + path: ./crypto_plugins/cs_monero/cs_monero_flutter_libs monero: git: diff --git a/scripts/app_config/templates/windows/CMakeLists.txt b/scripts/app_config/templates/windows/CMakeLists.txt index 795e592f2..b9add856d 100644 --- a/scripts/app_config/templates/windows/CMakeLists.txt +++ b/scripts/app_config/templates/windows/CMakeLists.txt @@ -86,27 +86,6 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_liblelantus/scripts/windows/build/libmobileliblelantus.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/monero/x86_64-w64-mingw32_libwallet2_api_c.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "monero_libwallet2_api_c.dll" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libwallet2_api_c.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "wownero_libwallet2_api_c.dll" - COMPONENT Runtime) - -#install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libgcc_s_seh-1.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libgcc_s_seh-1.dll" -# COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libpolyseed.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libpolyseed.dll" - COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libssp-0.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libssp-0.dll" - COMPONENT Runtime) - -#install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libstdc++-6.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libstdc++-6.dll" -# COMPONENT Runtime) - -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/monero_c/release/wownero/x86_64-w64-mingw32_libwinpthread-1.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "libwinpthread-1.dll" - COMPONENT Runtime) - if(PLUGIN_BUNDLED_LIBRARIES) install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" diff --git a/scripts/ios/build_all.sh b/scripts/ios/build_all.sh index b34724561..74e9721fa 100755 --- a/scripts/ios/build_all.sh +++ b/scripts/ios/build_all.sh @@ -16,7 +16,7 @@ rustup target add x86_64-apple-ios (cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_ios.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) diff --git a/scripts/ios/build_all_campfire.sh b/scripts/ios/build_all_campfire.sh index b34724561..74e9721fa 100755 --- a/scripts/ios/build_all_campfire.sh +++ b/scripts/ios/build_all_campfire.sh @@ -16,7 +16,7 @@ rustup target add x86_64-apple-ios (cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_ios.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) diff --git a/scripts/ios/build_all_duo.sh b/scripts/ios/build_all_duo.sh index 387f85f81..bef08e8c8 100755 --- a/scripts/ios/build_all_duo.sh +++ b/scripts/ios/build_all_duo.sh @@ -18,7 +18,7 @@ rustup target add x86_64-apple-ios (cd ../../crypto_plugins/flutter_liblelantus/scripts/ios && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/ios/ && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_ios.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) diff --git a/scripts/linux/build_all.sh b/scripts/linux/build_all.sh index c9542798e..d541cc34f 100755 --- a/scripts/linux/build_all.sh +++ b/scripts/linux/build_all.sh @@ -14,7 +14,7 @@ mkdir -p build ./build_secure_storage_deps.sh (cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_linux.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) diff --git a/scripts/linux/build_all_campfire.sh b/scripts/linux/build_all_campfire.sh index c9542798e..d541cc34f 100755 --- a/scripts/linux/build_all_campfire.sh +++ b/scripts/linux/build_all_campfire.sh @@ -14,7 +14,7 @@ mkdir -p build ./build_secure_storage_deps.sh (cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_linux.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) diff --git a/scripts/linux/build_all_duo.sh b/scripts/linux/build_all_duo.sh index e7397bdc5..5dc0e5bb0 100755 --- a/scripts/linux/build_all_duo.sh +++ b/scripts/linux/build_all_duo.sh @@ -16,7 +16,7 @@ mkdir -p build ./build_secure_storage_deps.sh & (cd ../../crypto_plugins/flutter_liblelantus/scripts/linux && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/linux && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_linux.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) diff --git a/scripts/macos/build_all.sh b/scripts/macos/build_all.sh index e3a58b45f..1636db4d3 100755 --- a/scripts/macos/build_all.sh +++ b/scripts/macos/build_all.sh @@ -8,7 +8,7 @@ set_rust_to_1671 (cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_macos.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) diff --git a/scripts/macos/build_all_campfire.sh b/scripts/macos/build_all_campfire.sh index e3a58b45f..1636db4d3 100755 --- a/scripts/macos/build_all_campfire.sh +++ b/scripts/macos/build_all_campfire.sh @@ -8,7 +8,7 @@ set_rust_to_1671 (cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_macos.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) diff --git a/scripts/macos/build_all_duo.sh b/scripts/macos/build_all_duo.sh index 6f70f4371..7cc31c9d1 100755 --- a/scripts/macos/build_all_duo.sh +++ b/scripts/macos/build_all_duo.sh @@ -10,7 +10,7 @@ set_rust_to_1671 (cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) (cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_macos.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) diff --git a/scripts/windows/build_all.sh b/scripts/windows/build_all.sh index fc6ac37b1..d63e408ce 100755 --- a/scripts/windows/build_all.sh +++ b/scripts/windows/build_all.sh @@ -9,7 +9,7 @@ set_rust_to_1671 mkdir -p build (cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) (cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_windows.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) diff --git a/scripts/windows/build_all_campfire.sh b/scripts/windows/build_all_campfire.sh index fc6ac37b1..d63e408ce 100755 --- a/scripts/windows/build_all_campfire.sh +++ b/scripts/windows/build_all_campfire.sh @@ -9,7 +9,7 @@ set_rust_to_1671 mkdir -p build (cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) (cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_windows.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) diff --git a/scripts/windows/build_all_duo.sh b/scripts/windows/build_all_duo.sh index 80303f28b..7226c4e41 100755 --- a/scripts/windows/build_all_duo.sh +++ b/scripts/windows/build_all_duo.sh @@ -11,7 +11,7 @@ set_rust_to_1671 mkdir -p build (cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) (cd ../../crypto_plugins/flutter_liblelantus/scripts/windows && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmonero/scripts/windows && ./build_all.sh) +(cd ../../crypto_plugins/cs_monero/tools/build_scripts && ./build_windows.sh ) set_rust_to_1720 (cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index bc627d626..36adbeb1a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("CameraWindows")); ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + CsMoneroFlutterLibsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CsMoneroFlutterLibsPluginCApi")); DesktopDropPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("DesktopDropPlugin")); FlutterLibepiccashPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index d25497442..041b049af 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST camera_windows connectivity_plus + cs_monero_flutter_libs desktop_drop flutter_libepiccash flutter_secure_storage_windows