diff --git a/crypto_plugins/flutter_libmwc b/crypto_plugins/flutter_libmwc index 931062f80d..d74b89b758 160000 --- a/crypto_plugins/flutter_libmwc +++ b/crypto_plugins/flutter_libmwc @@ -1 +1 @@ -Subproject commit 931062f80d5da745ff1535a1ad03ecaae3f87c15 +Subproject commit d74b89b75863ce2d20e54c9c28c4c18111184e28 diff --git a/lib/db/db_version_migration.dart b/lib/db/db_version_migration.dart index ab2b8bcde1..8b56d4e0b2 100644 --- a/lib/db/db_version_migration.dart +++ b/lib/db/db_version_migration.dart @@ -170,12 +170,11 @@ class DbVersionMigrator with WalletDB { final count = await MainDB.instance.isar.addresses.count(); // add change/receiving tags to address labels for (var i = 0; i < count; i += 50) { - final addresses = - await MainDB.instance.isar.addresses - .where() - .offset(i) - .limit(50) - .findAll(); + final addresses = await MainDB.instance.isar.addresses + .where() + .offset(i) + .limit(50) + .findAll(); final List labels = []; for (final address in addresses) { @@ -203,14 +202,13 @@ class DbVersionMigrator with WalletDB { // update/create label if tags is not empty if (tags != null) { - isar_models.AddressLabel? label = - await MainDB.instance.isar.addressLabels - .where() - .addressStringWalletIdEqualTo( - address.value, - address.walletId, - ) - .findFirst(); + isar_models.AddressLabel? label = await MainDB + .instance + .isar + .addressLabels + .where() + .addressStringWalletIdEqualTo(address.value, address.walletId) + .findFirst(); if (label == null) { label = isar_models.AddressLabel( walletId: address.walletId, @@ -268,13 +266,12 @@ class DbVersionMigrator with WalletDB { Bitcoincash(CryptoCurrencyNetwork.main).identifier || info.coinIdentifier == Bitcoincash(CryptoCurrencyNetwork.test).identifier) { - final ids = - await MainDB.instance - .getAddresses(walletId) - .filter() - .typeEqualTo(isar_models.AddressType.p2sh) - .idProperty() - .findAll(); + final ids = await MainDB.instance + .getAddresses(walletId) + .filter() + .typeEqualTo(isar_models.AddressType.p2sh) + .idProperty() + .findAll(); await MainDB.instance.isar.writeTxn(() async { await MainDB.instance.isar.addresses.deleteAll(ids); @@ -376,6 +373,20 @@ class DbVersionMigrator with WalletDB { // try to continue migrating return await migrate(15, secureStore: secureStore); + case 15: + // No-op: nodeApiSecret field added to NodeModel (Hive field 15). + // Existing nodes read null; updateDefaults() backfills from defaultNode. + + // update version + await DB.instance.put( + boxName: DB.boxNameDBInfo, + key: "hive_data_version", + value: 16, + ); + + // try to continue migrating + return await migrate(16, secureStore: secureStore); + default: // finally return return; @@ -421,17 +432,15 @@ class DbVersionMigrator with WalletDB { walletId: walletId, txid: tx.txid, timestamp: tx.timestamp, - type: - isIncoming - ? isar_models.TransactionType.incoming - : isar_models.TransactionType.outgoing, + type: isIncoming + ? isar_models.TransactionType.incoming + : isar_models.TransactionType.outgoing, subType: isar_models.TransactionSubType.none, amount: tx.amount, - amountString: - Amount( - rawValue: BigInt.from(tx.amount), - fractionDigits: epic.fractionDigits, - ).toJsonString(), + amountString: Amount( + rawValue: BigInt.from(tx.amount), + fractionDigits: epic.fractionDigits, + ).toJsonString(), fee: tx.fees, height: tx.height, isCancelled: tx.isCancelled, @@ -453,14 +462,12 @@ class DbVersionMigrator with WalletDB { publicKey: [], derivationIndex: isIncoming ? rcvIndex : -1, derivationPath: null, - type: - isIncoming - ? isar_models.AddressType.mimbleWimble - : isar_models.AddressType.unknown, - subType: - isIncoming - ? isar_models.AddressSubType.receiving - : isar_models.AddressSubType.unknown, + type: isIncoming + ? isar_models.AddressType.mimbleWimble + : isar_models.AddressType.unknown, + subType: isIncoming + ? isar_models.AddressSubType.receiving + : isar_models.AddressSubType.unknown, ); transactionsData.add(Tuple2(iTx, address)); } @@ -518,28 +525,25 @@ class DbVersionMigrator with WalletDB { final crypto = AppConfig.getCryptoCurrencyFor(info.coinIdentifier)!; for (var i = 0; i < count; i += 50) { - final txns = - await MainDB.instance - .getTransactions(walletId) - .offset(i) - .limit(50) - .findAll(); + final txns = await MainDB.instance + .getTransactions(walletId) + .offset(i) + .limit(50) + .findAll(); // migrate amount to serialized amount string - final txnsData = - txns - .map( - (tx) => Tuple2( - tx - ..amountString = - Amount( - rawValue: BigInt.from(tx.amount), - fractionDigits: crypto.fractionDigits, - ).toJsonString(), - tx.address.value, - ), - ) - .toList(); + final txnsData = txns + .map( + (tx) => Tuple2( + tx + ..amountString = Amount( + rawValue: BigInt.from(tx.amount), + fractionDigits: crypto.fractionDigits, + ).toJsonString(), + tx.address.value, + ), + ) + .toList(); // update db records await MainDB.instance.addNewTransactionData(txnsData, walletId); diff --git a/lib/models/node_model.dart b/lib/models/node_model.dart index 5d43d84dba..5386cae0f9 100644 --- a/lib/models/node_model.dart +++ b/lib/models/node_model.dart @@ -47,6 +47,8 @@ class NodeModel { final bool forceNoTor; // @HiveField(14) final bool isPrimary; + // @HiveField(15) + final String? nodeApiSecret; NodeModel({ required this.host, @@ -64,6 +66,7 @@ class NodeModel { this.forceNoTor = false, this.loginName, this.trusted, + this.nodeApiSecret, }); NodeModel copyWith({ @@ -81,6 +84,7 @@ class NodeModel { bool? forceNoTor, bool? clearnetEnabled, bool? isPrimary, + String? nodeApiSecret, }) { return NodeModel( host: host ?? this.host, @@ -98,6 +102,7 @@ class NodeModel { clearnetEnabled: clearnetEnabled ?? this.clearnetEnabled, forceNoTor: forceNoTor ?? this.forceNoTor, isPrimary: isPrimary ?? this.isPrimary, + nodeApiSecret: nodeApiSecret ?? this.nodeApiSecret, ); } @@ -123,6 +128,7 @@ class NodeModel { map['clearEnabled'] = clearnetEnabled; map['forceNoTor'] = forceNoTor; map['isPrimary'] = isPrimary; + map['nodeApiSecret'] = nodeApiSecret; return map; } diff --git a/lib/models/type_adaptors/node_model.g.dart b/lib/models/type_adaptors/node_model.g.dart index 218731a69c..1ab826d553 100644 --- a/lib/models/type_adaptors/node_model.g.dart +++ b/lib/models/type_adaptors/node_model.g.dart @@ -32,13 +32,14 @@ class NodeModelAdapter extends TypeAdapter { clearnetEnabled: fields[12] as bool? ?? true, forceNoTor: fields[13] as bool? ?? false, isPrimary: fields[14] as bool? ?? false, + nodeApiSecret: fields[15] as String?, ); } @override void write(BinaryWriter writer, NodeModel obj) { writer - ..writeByte(15) + ..writeByte(16) ..writeByte(0) ..write(obj.id) ..writeByte(1) @@ -68,7 +69,9 @@ class NodeModelAdapter extends TypeAdapter { ..writeByte(13) ..write(obj.forceNoTor) ..writeByte(14) - ..write(obj.isPrimary); + ..write(obj.isPrimary) + ..writeByte(15) + ..write(obj.nodeApiSecret); } @override 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 b873664535..bd009f1710 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 @@ -259,6 +259,7 @@ class _AddEditNodeViewState extends ConsumerState { clearnetEnabled: plainEnabled, forceNoTor: forceNoTor, isPrimary: false, + nodeApiSecret: formData.apiSecret, ); await ref @@ -288,6 +289,7 @@ class _AddEditNodeViewState extends ConsumerState { clearnetEnabled: plainEnabled, forceNoTor: forceNoTor, isPrimary: formData.isPrimary ?? false, + nodeApiSecret: formData.apiSecret, ); await ref @@ -750,7 +752,7 @@ class _AddEditNodeViewState extends ConsumerState { } class NodeFormData { - String? name, host, login, password; + String? name, host, login, password, apiSecret; int? port; bool? useSSL, isFailover, trusted, forceNoTor, isPrimary; TorPlainNetworkOption? netOption; @@ -791,12 +793,14 @@ class _NodeFormState extends ConsumerState { late final TextEditingController _portController; late final TextEditingController _passwordController; late final TextEditingController _usernameController; + late final TextEditingController _apiSecretController; final _nameFocusNode = FocusNode(); final _passwordFocusNode = FocusNode(); final _portFocusNode = FocusNode(); final _hostFocusNode = FocusNode(); final _usernameFocusNode = FocusNode(); + final _apiSecretFocusNode = FocusNode(); bool _useSSL = false; bool _isFailover = false; @@ -858,6 +862,9 @@ class _NodeFormState extends ConsumerState { ref.read(nodeFormDataProvider).password = _passwordController.text.isEmpty ? null : _passwordController.text; + ref.read(nodeFormDataProvider).apiSecret = _apiSecretController.text.isEmpty + ? null + : _apiSecretController.text; ref.read(nodeFormDataProvider).port = port; ref.read(nodeFormDataProvider).useSSL = _useSSL; ref.read(nodeFormDataProvider).isFailover = _isFailover; @@ -876,6 +883,7 @@ class _NodeFormState extends ConsumerState { _portController = TextEditingController(); _passwordController = TextEditingController(); _usernameController = TextEditingController(); + _apiSecretController = TextEditingController(); enableAuthFields = _checkShouldEnableAuthFields(widget.coin); @@ -897,6 +905,7 @@ class _NodeFormState extends ConsumerState { _hostController.text = node.host; _portController.text = node.port.toString(); _usernameController.text = node.loginName ?? ""; + _apiSecretController.text = node.nodeApiSecret ?? ""; _useSSL = node.useSSL; _isFailover = node.isFailover; _trusted = node.trusted ?? false; @@ -940,12 +949,14 @@ class _NodeFormState extends ConsumerState { _portController.dispose(); _passwordController.dispose(); _usernameController.dispose(); + _apiSecretController.dispose(); _nameFocusNode.dispose(); _passwordFocusNode.dispose(); _usernameFocusNode.dispose(); _hostFocusNode.dispose(); _portFocusNode.dispose(); + _apiSecretFocusNode.dispose(); super.dispose(); } @@ -1244,6 +1255,54 @@ class _NodeFormState extends ConsumerState { ), ), if (enableAuthFields) const SizedBox(height: 8), + if (widget.coin is Mimblewimblecoin) + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + controller: _apiSecretController, + readOnly: shouldBeReadOnly, + enabled: enableField(_apiSecretController), + obscureText: true, + focusNode: _apiSecretFocusNode, + style: STextStyles.field(context), + decoration: + standardInputDecoration( + "API secret (optional)", + _apiSecretFocusNode, + context, + ).copyWith( + suffixIcon: + !shouldBeReadOnly && + _apiSecretController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + _apiSecretController.text = ""; + _updateState(); + }, + ), + ], + ), + ), + ) + : null, + ), + onChanged: (newValue) { + _updateState(); + setState(() {}); + }, + ), + ), + if (widget.coin is Mimblewimblecoin) const SizedBox(height: 8), if (widget.coin is! CryptonoteCurrency) Row( children: [ diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 69205f1f0c..f7b495ee06 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -40,7 +40,7 @@ abstract class Constants { // Enable Logger.print statements static const bool disableLogger = false; - static const int currentDataVersion = 15; + static const int currentDataVersion = 16; static const int rescanV1 = 1; diff --git a/lib/utilities/test_mwcmqs_connection.dart b/lib/utilities/test_mwcmqs_connection.dart index c199afcbf3..48860df3c1 100644 --- a/lib/utilities/test_mwcmqs_connection.dart +++ b/lib/utilities/test_mwcmqs_connection.dart @@ -17,15 +17,13 @@ import '../services/tor_service.dart'; import 'logger.dart'; import 'prefs.dart'; -Future _testMwcMqsNodeConnection(Uri uri) async { +Future _testMwcMqsNodeConnection(Uri uri, {String? apiSecret}) async { final HTTP client = HTTP(); 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')); + if (apiSecret != null) { + final credentials = base64Encode(utf8.encode('mwcmain:$apiSecret')); headers['Authorization'] = 'Basic $credentials'; } final response = await client @@ -80,7 +78,7 @@ Future testMwcNodeConnection(NodeFormData data) async { uri = uri.replace(port: data.port); try { - if (await _testMwcMqsNodeConnection(uri)) { + if (await _testMwcMqsNodeConnection(uri, apiSecret: data.apiSecret)) { return data; } else { return null; diff --git a/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart b/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart index c9d57878a0..2ed274b35c 100644 --- a/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart +++ b/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart @@ -101,6 +101,7 @@ class Mimblewimblecoin extends Bip39Currency { torEnabled: true, clearnetEnabled: true, isPrimary: true, + nodeApiSecret: '11ne3EAUtOXVKwhxm84U', ); default: diff --git a/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart b/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart index 3850cb7500..a5f74581e3 100644 --- a/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart +++ b/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart @@ -41,9 +41,15 @@ class MimblewimblecoinWallet extends Bip39Wallet { : super(Mimblewimblecoin(network)); final syncMutex = Mutex(); + final _walletOpenMutex = Mutex(); NodeModel? _mimblewimblecoinNode; Timer? timer; + static bool _mwcLogsInitialized = false; + + // Process-scoped Rust pointer; do not persist. + String? _walletHandle; + double highestPercent = 0; Future get getSyncPercent async { final int lastScannedBlock = @@ -75,12 +81,8 @@ class MimblewimblecoinWallet extends Bip39Wallet { value: stringConfig, ); - // Restart MWCMQS listener with new configuration if wallet has a handle. try { - final handle = await secureStorageInterface.read( - key: '${walletId}_wallet', - ); - if (handle != null && handle.isNotEmpty) { + if (_walletHandle != null) { await stopSlatepackListener(); await startSlatepackListener(); Logging.instance.i( @@ -95,32 +97,43 @@ class MimblewimblecoinWallet extends Bip39Wallet { } Future _ensureWalletOpen() async { - final existing = await secureStorageInterface.read( - key: '${walletId}_wallet', - ); - if (existing != null && existing.isNotEmpty) return existing; + return await _walletOpenMutex.protect(() async { + final cached = _walletHandle; + if (cached != null && cached.isNotEmpty) return cached; - final config = await _getRealConfig(); - final password = await secureStorageInterface.read( - key: '${walletId}_password', - ); - if (password == null) { - throw Exception('Wallet password not found'); - } - final opened = await libMwc.openWallet(config: config, password: password); - await secureStorageInterface.write( - key: '${walletId}_wallet', - value: opened, - ); - return opened; + // Drop stale pointer left by pre-instance-var builds. + await secureStorageInterface.delete(key: '${walletId}_wallet'); + + final config = await _getRealConfig(); + if (!_mwcLogsInitialized) { + try { + await libMwc.initLogs(config: config); + _mwcLogsInitialized = true; + } catch (e, s) { + Logging.instance.w("libMwc.initLogs failed: $e\n$s"); + } + } + final password = await secureStorageInterface.read( + key: '${walletId}_password', + ); + if (password == null) { + throw Exception('Wallet password not found'); + } + final opened = await libMwc + .openWallet(config: config, password: password) + .timeout( + const Duration(seconds: 60), + onTimeout: () => throw TimeoutException('openWallet timed out'), + ); + _walletHandle = opened; + return opened; + }); } /// Returns an empty String on success, error message on failure. Future cancelPendingTransactionAndPost(String txSlateId) async { try { - final String wallet = (await secureStorageInterface.read( - key: '${walletId}_wallet', - ))!; + final String wallet = await _ensureWalletOpen(); final result = await libMwc.cancelTransaction( wallet: wallet, @@ -253,9 +266,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { /// Decode a slatepack. Future decodeSlatepack(String slatepack) async { try { - final handle = await secureStorageInterface.read( - key: '${walletId}_wallet', - ); + final handle = _walletHandle; final result = handle != null ? await libMwc.decodeSlatepackWithWallet( wallet: handle, @@ -346,13 +357,10 @@ class MimblewimblecoinWallet extends Bip39Wallet { /// Start MWCMQS listener for automatic transaction processing. Future startSlatepackListener() async { try { - await _ensureWalletOpen(); + final wallet = await _ensureWalletOpen(); final mwcmqsConfig = await getMwcMqsConfig(); - final wallet = await secureStorageInterface.read( - key: '${walletId}_wallet', - ); libMwc.startMwcMqsListener( - wallet: wallet!, + wallet: wallet, mwcmqsConfig: mwcmqsConfig.toString(), ); } catch (e, s) { @@ -409,10 +417,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { > analyzeSlatepack(String slatepack) async { try { - // Get wallet handle if available - final wallet = await secureStorageInterface.read( - key: '${walletId}_wallet', - ); + final wallet = _walletHandle; // Decode the slatepack final decoded = wallet != null @@ -563,6 +568,17 @@ class MimblewimblecoinWallet extends Bip39Wallet { // ================= Private ================================================= + Future _ensureApiSecret(String walletDir) async { + final file = File('$walletDir/.api_secret'); + final secret = _mimblewimblecoinNode?.nodeApiSecret; + if (secret != null) { + await Directory(walletDir).create(recursive: true); + await file.writeAsString(secret); + } else if (await file.exists()) { + await file.delete(); + } + } + Future _getConfig() async { if (_mimblewimblecoinNode == null) { await updateNode(); @@ -576,6 +592,8 @@ class MimblewimblecoinWallet extends Bip39Wallet { final String nodeApiAddress = uri.toString(); final walletDir = await _currentWalletDirPath(); + await _ensureApiSecret(walletDir); + final Map config = {}; config["wallet_dir"] = walletDir; config["check_node_api_http_addr"] = nodeApiAddress; @@ -597,11 +615,11 @@ class MimblewimblecoinWallet extends Bip39Wallet { int satoshiAmount, { bool ifErrorEstimateFee = false, }) async { - final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); + final wallet = await _ensureWalletOpen(); try { final available = info.cachedBalance.spendable.raw.toInt(); final transactionFees = await libMwc.getTransactionFees( - wallet: wallet!, + wallet: wallet, amount: satoshiAmount, minimumConfirmations: cryptoCurrency.minConfirms, available: available, @@ -625,13 +643,13 @@ class MimblewimblecoinWallet extends Bip39Wallet { Future _startSync() async { Logging.instance.i("request start sync"); - final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); + final wallet = await _ensureWalletOpen(); const int refreshFromNode = 1; if (!syncMutex.isLocked) { await syncMutex.protect(() async { // How does getWalletBalances start syncing???? await libMwc.getWalletBalances( - wallet: wallet!, + wallet: wallet, refreshFromNode: refreshFromNode, minimumConfirmations: 10, ); @@ -650,10 +668,10 @@ class MimblewimblecoinWallet extends Bip39Wallet { }) > _allWalletBalances() async { - final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); + final wallet = await _ensureWalletOpen(); const refreshFromNode = 0; return await libMwc.getWalletBalances( - wallet: wallet!, + wallet: wallet, refreshFromNode: refreshFromNode, minimumConfirmations: cryptoCurrency.minConfirms, ); @@ -718,10 +736,10 @@ class MimblewimblecoinWallet extends Bip39Wallet { int index, MwcMqsConfigModel mwcmqsConfig, ) async { - final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); + final wallet = await _ensureWalletOpen(); final walletAddress = await libMwc.getAddressInfo( - wallet: wallet!, + wallet: wallet, index: index, ); @@ -744,9 +762,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { try { //First stop the current listener libMwc.stopMwcMqsListener(); - final wallet = await secureStorageInterface.read( - key: '${walletId}_wallet', - ); + final wallet = await _ensureWalletOpen(); // max number of blocks to scan per loop iteration const scanChunkSize = 10000; @@ -766,7 +782,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); final int nextScannedBlock = await libMwc.scanOutputs( - wallet: wallet!, + wallet: wallet, startHeight: lastScannedBlock, numberOfBlocks: scanChunkSize, ); @@ -798,10 +814,10 @@ class MimblewimblecoinWallet extends Bip39Wallet { Future _listenToMwcmqs() async { Logging.instance.i("STARTING WALLET LISTENER ...."); - final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); + final wallet = await _ensureWalletOpen(); final MwcMqsConfigModel mwcmqsConfig = await getMwcMqsConfig(); libMwc.startMwcMqsListener( - wallet: wallet!, + wallet: wallet, mwcmqsConfig: mwcmqsConfig.toString(), ); } @@ -855,22 +871,19 @@ class MimblewimblecoinWallet extends Bip39Wallet { @override Future init({bool? isRestore}) async { if (isRestore != true) { - String? encodedWallet = await secureStorageInterface.read( - key: "${walletId}_wallet", + // Password presence is the durable "wallet provisioned" marker; the + // old wallet-handle marker was process-scoped. + final existingPassword = await secureStorageInterface.read( + key: '${walletId}_password', ); - // check if should create a new wallet - if (encodedWallet == null) { + if (existingPassword == null) { await updateNode(); final mnemonicString = await getMnemonic(); final String password = generatePassword(); final String stringConfig = await _getConfig(); final MwcMqsConfigModel mwcmqsConfig = await getMwcMqsConfig(); - //if (!_logsInitialized) { - // await libMwc.initLogs(config: stringConfig); - // _logsInitialized = true; // Set flag to true after initializing - // } await secureStorageInterface.write( key: '${walletId}_config', value: stringConfig, @@ -894,14 +907,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); //Open wallet - encodedWallet = await libMwc.openWallet( - config: stringConfig, - password: password, - ); - await secureStorageInterface.write( - key: '${walletId}_wallet', - value: encodedWallet, - ); + await _ensureWalletOpen(); //Store MwcMqs address info await _generateAndStoreReceivingAddressForIndex(0); @@ -926,23 +932,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); } else { try { - final config = await _getRealConfig(); - //if (!_logsInitialized) { - // await libMwc.initLogs(config: config); - // _logsInitialized = true; // Set flag to true after initializing - //} - final password = await secureStorageInterface.read( - key: '${walletId}_password', - ); - - final walletOpen = await libMwc.openWallet( - config: config, - password: password!, - ); - await secureStorageInterface.write( - key: '${walletId}_wallet', - value: walletOpen, - ); + await _ensureWalletOpen(); await updateNode(); } catch (e, s) { @@ -958,9 +948,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { @override Future confirmSend({required TxData txData}) async { try { - final wallet = await secureStorageInterface.read( - key: '${walletId}_wallet', - ); + final wallet = await _ensureWalletOpen(); final MwcMqsConfigModel mwcmqsConfig = await getMwcMqsConfig(); // TODO determine whether it is worth sending change to a change address. @@ -983,7 +971,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { if (receiverAddress.startsWith("http://") || receiverAddress.startsWith("https://")) { transaction = await libMwc.txHttpSend( - wallet: wallet!, + wallet: wallet, selectionStrategyIsAll: 0, minimumConfirmations: cryptoCurrency.minConfirms, message: txData.noteOnChain ?? "", @@ -992,7 +980,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); } else if (receiverAddress.startsWith("mwcmqs://")) { transaction = await libMwc.createTransaction( - wallet: wallet!, + wallet: wallet, amount: txData.recipients!.first.amount.raw.toInt(), address: txData.recipients!.first.address, secretKeyIndex: 0, @@ -1144,14 +1132,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); //Open Wallet - final walletOpen = await libMwc.openWallet( - config: stringConfig, - password: password, - ); - await secureStorageInterface.write( - key: '${walletId}_wallet', - value: walletOpen, - ); + await _ensureWalletOpen(); await _generateAndStoreReceivingAddressForIndex( mimblewimblecoinData.receivingIndex, @@ -1317,9 +1298,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { @override Future updateTransactions() async { try { - final wallet = await secureStorageInterface.read( - key: '${walletId}_wallet', - ); + final wallet = await _ensureWalletOpen(); const refreshFromNode = 1; final myAddresses = await mainDB @@ -1335,7 +1314,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { final myAddressesSet = myAddresses.toSet(); final transactions = await libMwc.getTransactions( - wallet: wallet!, + wallet: wallet, refreshFromNode: refreshFromNode, ); @@ -1505,7 +1484,8 @@ class MimblewimblecoinWallet extends Bip39Wallet { NodeFormData() ..host = node!.host ..useSSL = node.useSSL - ..port = node.port, + ..port = node.port + ..apiSecret = node.nodeApiSecret, ) != null; } catch (e, s) { @@ -1570,8 +1550,12 @@ Future deleteMimblewimblecoinWallet({ required String walletId, required SecureStorageInterface secureStore, }) async { - final wallet = await secureStore.read(key: '${walletId}_wallet'); + await secureStore.delete(key: '${walletId}_wallet'); + String? config = await secureStore.read(key: '${walletId}_config'); + if (config == null) { + return "Tried to delete non existent mimblewimblecoin wallet file with walletId=$walletId"; + } if (Platform.isIOS) { final Directory appDir = await StackFileSystem.applicationRootDirectory(); @@ -1579,20 +1563,17 @@ Future deleteMimblewimblecoinWallet({ final String name = walletId.trim(); final walletDir = '$path/$name'; - final editConfig = jsonDecode(config as String); + final editConfig = jsonDecode(config); editConfig["wallet_dir"] = walletDir; config = jsonEncode(editConfig); } - if (wallet == null) { - return "Tried to delete non existent mimblewimblecoin wallet file with walletId=$walletId"; - } else { - try { - return libMwc.deleteWallet(wallet: wallet, config: config!); - } catch (e, s) { - Logging.instance.e("$e\n$s"); - return "deleteMimblewimblecoinWallet($walletId) failed..."; - } + try { + // Rust deleteWallet ignores the handle param. + return libMwc.deleteWallet(wallet: "", config: config); + } catch (e, s) { + Logging.instance.e("$e\n$s"); + return "deleteMimblewimblecoinWallet($walletId) failed..."; } } diff --git a/lib/wl_gen/interfaces/libmwc_interface.dart b/lib/wl_gen/interfaces/libmwc_interface.dart index acd5b2d795..a44b3b4d20 100644 --- a/lib/wl_gen/interfaces/libmwc_interface.dart +++ b/lib/wl_gen/interfaces/libmwc_interface.dart @@ -140,6 +140,8 @@ abstract class LibMwcInterface { Future deleteWallet({required String wallet, required String config}); + Future initLogs({required String config}); + String getPluginVersion(); } diff --git a/tool/wl_templates/MWC_libmwc_interface_impl.template.dart b/tool/wl_templates/MWC_libmwc_interface_impl.template.dart index 0262cc5e35..c88c1662e1 100644 --- a/tool/wl_templates/MWC_libmwc_interface_impl.template.dart +++ b/tool/wl_templates/MWC_libmwc_interface_impl.template.dart @@ -117,6 +117,11 @@ final class _LibMwcInterfaceImpl extends LibMwcInterface { return mimblewimblecoin.Libmwc.getChainHeight(config: config); } + @override + Future initLogs({required String config}) { + return mimblewimblecoin.Libmwc.initLogs(config: config); + } + @override Future<({int fee, bool strategyUseAll, int total})> getTransactionFees({ required String wallet,