diff --git a/crypto_plugins/flutter_libmwc b/crypto_plugins/flutter_libmwc index 19764f5c8..fe78fd452 160000 --- a/crypto_plugins/flutter_libmwc +++ b/crypto_plugins/flutter_libmwc @@ -1 +1 @@ -Subproject commit 19764f5c8adbf93567962ea94e9ff146205aaf45 +Subproject commit fe78fd45284f99f26a40bba15e8d1a53c718be1a diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart index 1071ffdae..777f2b75e 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart @@ -15,6 +15,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/wallets/wallet/impl/mimblewimblecoin_wallet.dart'; import 'package:tuple/tuple.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -1730,7 +1731,7 @@ class _TransactionDetailsViewState ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - floatingActionButton: (coin is Epiccash && + floatingActionButton: ((coin is Epiccash || coin is Mimblewimblecoin) && _transaction.getConfirmations(currentHeight) < 1 && _transaction.isCancelled == false) ? ConditionalParent( @@ -1775,6 +1776,59 @@ class _TransactionDetailsViewState ), ); + final result = + await wallet.cancelPendingTransactionAndPost(id); + if (mounted) { + // pop progress dialog + Navigator.of(context).pop(); + + if (result.isEmpty) { + await showDialog( + context: context, + builder: (_) => StackOkDialog( + title: "Transaction cancelled", + onOkPressed: (_) { + wallet.refresh(); + Navigator.of(context).popUntil( + ModalRoute.withName( + WalletView.routeName, + ), + ); + }, + ), + ); + } else { + await showDialog( + context: context, + builder: (_) => StackOkDialog( + title: "Failed to cancel transaction", + message: result, + ), + ); + } + } + } else if (wallet is MimblewimblecoinWallet) { + final String? id = _transaction.slateId; + if (id == null) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Could not find MWC transaction ID", + context: context, + ), + ); + return; + } + + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => + const CancellingTransactionProgressDialog(), + ), + ); + final result = await wallet.cancelPendingTransactionAndPost(id); if (mounted) { @@ -1810,7 +1864,7 @@ class _TransactionDetailsViewState unawaited( showFloatingFlushBar( type: FlushBarType.warning, - message: "ERROR: Wallet type is not Epic Cash", + message: "ERROR: Wallet type is not Epic Cash or MimbleWimbleCoin", context: context, ), ); diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart index 03316599f..737b5e660 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart @@ -2112,18 +2112,7 @@ class _TransactionV2DetailsViewState ); } } - } else { - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "ERROR: Wallet type is not Epic Cash", - context: context, - ), - ); - return; - } - - if (wallet is MimblewimblecoinWallet) { + } else if (wallet is MimblewimblecoinWallet) { final String? id = _transaction.slateId; if (id == null) { unawaited( @@ -2181,8 +2170,7 @@ class _TransactionV2DetailsViewState unawaited( showFloatingFlushBar( type: FlushBarType.warning, - message: - "ERROR: Wallet type is not Mimblewimblecoin", + message: "ERROR: Wallet type is not Epic Cash or MimbleWimbleCoin", context: context, ), ); diff --git a/lib/utilities/test_mwcmqs_connection.dart b/lib/utilities/test_mwcmqs_connection.dart index 44433a103..cfcd4a098 100644 --- a/lib/utilities/test_mwcmqs_connection.dart +++ b/lib/utilities/test_mwcmqs_connection.dart @@ -18,11 +18,21 @@ import 'prefs.dart'; Future _testMwcMqsNodeConnection(Uri uri) async { final HTTP client = HTTP(); - try { + try { + final headers = { + 'Content-Type': 'application/json', + }; + + if (uri.toString() == 'https://mwc713.mwc.mw/v1/version') { + const username = 'mwcmain'; + const password = '11ne3EAUtOXVKwhxm84U'; + final credentials = base64Encode(utf8.encode('$username:$password')); + headers['Authorization'] = 'Basic $credentials'; + } final response = await client .get( url: uri, - headers: {'Content-Type': 'application/json'}, + headers: headers, proxyInfo: Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, @@ -67,7 +77,7 @@ Future testMwcNodeConnection(NodeFormData data) async { Uri uri = Uri.parse(data.host! + path_postfix); uri = uri.replace(port: data.port); - + try { if (await _testMwcMqsNodeConnection(uri)) { return data; diff --git a/lib/utilities/test_node_connection.dart b/lib/utilities/test_node_connection.dart index e25289587..704315953 100644 --- a/lib/utilities/test_node_connection.dart +++ b/lib/utilities/test_node_connection.dart @@ -18,6 +18,7 @@ import '../wallets/wallet/impl/solana_wallet.dart'; import 'connection_check/electrum_connection_check.dart'; import 'logger.dart'; import 'test_epic_box_connection.dart'; +import 'test_mwcmqs_connection.dart'; import 'test_eth_node_connection.dart'; import 'test_monero_node_connection.dart'; import 'test_stellar_node_connection.dart'; @@ -104,6 +105,19 @@ Future testNodeConnection({ } break; + case Mimblewimblecoin(): + try { + final data = await testMwcNodeConnection(formData); + + if (data != null) { + testPassed = true; + onSuccess?.call(data); + } + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Warning); + } + break; + case CryptonoteCurrency(): try { final proxyInfo = ref.read(prefsChangeNotifierProvider).useTor diff --git a/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart b/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart index 9ea295dd4..3127ef035 100644 --- a/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart +++ b/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart @@ -50,36 +50,33 @@ class Mimblewimblecoin extends Bip39Currency { // change this to change the number of confirms a tx needs in order to show as confirmed int get minConfirms => 3; - @override - bool validateAddress(String address) { - // Invalid address that contains HTTP and mwcmqs domain - if ((address.startsWith("http://") || address.startsWith("https://")) && - address.contains("@")) { - return false; - } - if (address.startsWith("http://") || address.startsWith("https://")) { - if (Uri.tryParse(address) != null) { - return true; - } - } - - return mimblewimblecoin.Libmwc.validateSendAddress(address: address); + @override +bool validateAddress(String address) { + Uri? uri = Uri.tryParse(address); + if (uri != null && + (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "mwcmqs") && + uri.host.isNotEmpty && + !uri.host.endsWith(".onion")) { + return true; } + return mimblewimblecoin.Libmwc.validateSendAddress(address: address); +} + @override NodeModel get defaultNode { switch (network) { case CryptoCurrencyNetwork.main: return NodeModel( - host: "http://mwc713.mwc.mw", - port: 3413, + host: "https://mwc713.mwc.mw", + port: 443, name: DefaultNodes.defaultName, id: DefaultNodes.buildId(this), - useSSL: false, + useSSL: true, enabled: true, coinName: identifier, isFailover: true, - isDown: false, + isDown: false ); default: @@ -88,10 +85,10 @@ class Mimblewimblecoin extends Bip39Currency { } @override - int get defaultSeedPhraseLength => 24; + int get defaultSeedPhraseLength => 12; @override - int get fractionDigits => 8; + int get fractionDigits => 9; @override bool get hasBuySupport => false; @@ -100,13 +97,13 @@ class Mimblewimblecoin extends Bip39Currency { bool get hasMnemonicPassphraseSupport => false; @override - List get possibleMnemonicLengths => [defaultSeedPhraseLength, 12]; + List get possibleMnemonicLengths => [defaultSeedPhraseLength, 24]; @override AddressType get defaultAddressType => AddressType.mimbleWimble; @override - BigInt get satsPerCoin => BigInt.from(100000000); + BigInt get satsPerCoin => BigInt.from(1000000000); @override int get targetBlockTimeSeconds => 60; diff --git a/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart b/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart index 83d038e9d..5ce8a6b0e 100644 --- a/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart +++ b/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart @@ -38,6 +38,7 @@ import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; import '../supporting/mimblewimblecoin_wallet_info_extension.dart'; + // // refactor of https://github.com/cypherstack/stack_wallet/blob/1d9fb4cd069f22492ece690ac788e05b8f8b1209/lib/services/coins/epiccash/epiccash_wallet.dart // @@ -48,7 +49,8 @@ class MimblewimblecoinWallet extends Bip39Wallet { final syncMutex = Mutex(); NodeModel? _mimblewimblecoinNode; Timer? timer; - + bool _logsInitialized = false; + double highestPercent = 0; Future get getSyncPercent async { final int lastScannedBlock = @@ -147,7 +149,6 @@ class MimblewimblecoinWallet extends Bip39Wallet { final uri = Uri.parse(nodeAddress).replace(port: port); final String nodeApiAddress = uri.toString(); - final walletDir = await _currentWalletDirPath(); final Map config = {}; @@ -174,7 +175,6 @@ class MimblewimblecoinWallet extends Bip39Wallet { final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); try { final available = info.cachedBalance.spendable.raw.toInt(); - final transactionFees = await mimblewimblecoin.Libmwc.getTransactionFees( wallet: wallet!, amount: satoshiAmount, @@ -304,17 +304,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ) async { Address? address = await getCurrentReceivingAddress(); - if (address != null) { - final splitted = address.value.split('@'); - //Check if the address is the same as the current mwcmqs domain - //Since we're only using one epicbpox now this doesn't apply but will be - // useful in the future - final mwcmqsConfig = await getMwcMqsConfig(); - if (splitted[1] != mwcmqsConfig.host) { - //Update the address - address = await thisWalletAddress(index, mwcmqsConfig); - } - } else { + if (address == null) { final mwcmqsConfig = await getMwcMqsConfig(); address = await thisWalletAddress(index, mwcmqsConfig); } @@ -478,6 +468,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { @override Future init({bool? isRestore}) async { + if (isRestore != true) { String? encodedWallet = await secureStorageInterface.read(key: "${walletId}_wallet"); @@ -490,7 +481,10 @@ class MimblewimblecoinWallet extends Bip39Wallet { final String password = generatePassword(); final String stringConfig = await _getConfig(); final MwcMqsConfigModel mwcmqsConfig = await getMwcMqsConfig(); - + //if (!_logsInitialized) { + // await mimblewimblecoin.Libmwc.initLogs(config: stringConfig); + // _logsInitialized = true; // Set flag to true after initializing + // } await secureStorageInterface.write( key: '${walletId}_config', value: stringConfig, @@ -522,9 +516,8 @@ class MimblewimblecoinWallet extends Bip39Wallet { key: '${walletId}_wallet', value: encodedWallet, ); - //Store MwcMqs address info - // await _generateAndStoreReceivingAddressForIndex(0); + await _generateAndStoreReceivingAddressForIndex(0); // subtract a couple days to ensure we have a buffer for SWB final bufferedCreateHeight = _calculateRestoreHeightFrom( @@ -547,12 +540,11 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); } else { try { - Logging.instance.log( - "initializeExisting() ${cryptoCurrency.prettyName} wallet", - level: LogLevel.Info, - ); - final config = await _getRealConfig(); + //if (!_logsInitialized) { + // await mimblewimblecoin.Libmwc.initLogs(config: config); + // _logsInitialized = true; // Set flag to true after initializing + //} final password = await secureStorageInterface.read(key: '${walletId}_password'); @@ -590,21 +582,22 @@ class MimblewimblecoinWallet extends Bip39Wallet { final String receiverAddress = txData.recipients!.first.address; - if (!receiverAddress.startsWith("http://") || - !receiverAddress.startsWith("https://")) { - final bool isMwcmqsConnected = await _testMwcmqsServer( - mwcmqsConfig, - ); - if (!isMwcmqsConnected) { - throw Exception( - "Failed to send TX : Unable to reach mimblewimblecoin server"); - } - } + //if (!receiverAddress.startsWith("http://") || + // !receiverAddress.startsWith("https://")) { + // final bool isMwcmqsConnected = await _testMwcmqsServer( + // mwcmqsConfig, + // ); + // if (!isMwcmqsConnected) { + // throw Exception( + // "Failed to send TX : Unable to reach mimblewimblecoin server"); + // } + //} ({String commitId, String slateId}) transaction; if (receiverAddress.startsWith("http://") || - receiverAddress.startsWith("https://")) { + receiverAddress.startsWith("https://") || + receiverAddress.startsWith("mwcmqcs://")) { transaction = await mimblewimblecoin.Libmwc.txHttpSend( wallet: wallet!, selectionStrategyIsAll: 0, @@ -753,10 +746,10 @@ class MimblewimblecoinWallet extends Bip39Wallet { key: '${walletId}_wallet', value: walletOpen, ); - - // await _generateAndStoreReceivingAddressForIndex( - // mimblewimblecoinData.receivingIndex, - // ); + + await _generateAndStoreReceivingAddressForIndex( + mimblewimblecoinData.receivingIndex, + ); } }); @@ -799,7 +792,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { // this will always be zero???? final int curAdd = await _getCurrentIndex(); - // await _generateAndStoreReceivingAddressForIndex(curAdd); + await _generateAndStoreReceivingAddressForIndex(curAdd); await _startScans(); @@ -1087,23 +1080,19 @@ class MimblewimblecoinWallet extends Bip39Wallet { @override Future pingCheck() async { - return true; - //try { - // final node = nodeService.getPrimaryNodeFor(currency: cryptoCurrency); -// - // // force unwrap optional as we want connection test to fail if wallet - // // wasn't initialized or mwcmqs node was set to null - // return await testMwcNodeConnection( - // NodeFormData() - // ..host = node!.host - // ..useSSL = node.useSSL - // ..port = node.port, - // ) != - // null; - //} catch (e, s) { - // Logging.instance.log("$e\n$s", level: LogLevel.Info); - // return false; - //} + try { + final node = nodeService.getPrimaryNodeFor(currency: cryptoCurrency); + return await testMwcNodeConnection( + NodeFormData() + ..host = node!.host + ..useSSL = node.useSSL + ..port = node.port, + ) != + null; + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Info); + return false; + } } @override