From 8c082f3ed43d51cd66319fb3e6bb42d7a7044c1e Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 3 May 2024 18:04:57 -0600 Subject: [PATCH] untested stellar tor listener --- lib/wallets/wallet/impl/stellar_wallet.dart | 118 +++++++++++++++----- 1 file changed, 90 insertions(+), 28 deletions(-) diff --git a/lib/wallets/wallet/impl/stellar_wallet.dart b/lib/wallets/wallet/impl/stellar_wallet.dart index a2ad93083..657fd7676 100644 --- a/lib/wallets/wallet/impl/stellar_wallet.dart +++ b/lib/wallets/wallet/impl/stellar_wallet.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; import 'package:socks5_proxy/socks.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; @@ -11,6 +12,9 @@ import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart' import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart'; +import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/events/global/tor_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/tor_service.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; @@ -23,11 +27,47 @@ import 'package:stackwallet/wallets/wallet/intermediate/bip39_wallet.dart'; import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar; class StellarWallet extends Bip39Wallet { - StellarWallet(CryptoCurrencyNetwork network) : super(Stellar(network)); + StellarWallet(CryptoCurrencyNetwork network) : super(Stellar(network)) { + final bus = GlobalEventBus.instance; - stellar.StellarSDK get stellarSdk { - if (_stellarSdk == null) { - _updateSdk(); + // 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 { + _stellarSdk?.httpClient.close(); + _stellarSdk = null; + }, + ); + } + + Future get stellarSdk async { + if (_requireMutex) { + await _torConnectingLock.protect(() async { + _stellarSdk ??= _getFreshSdk(); + }); + } else { + _stellarSdk ??= _getFreshSdk(); } return _stellarSdk!; } @@ -44,19 +84,32 @@ class StellarWallet extends Bip39Wallet { } // ============== Private ==================================================== + // add finalizer to cancel stream subscription when all references to an + // instance of this becomes inaccessible + final _ = Finalizer( + (p0) { + p0._torPreferenceListener?.cancel(); + p0._torStatusListener?.cancel(); + }, + ); + + StreamSubscription? _torStatusListener; + StreamSubscription? _torPreferenceListener; + + final Mutex _torConnectingLock = Mutex(); + bool _requireMutex = false; stellar.StellarSDK? _stellarSdk; Future _getBaseFee() async { - final fees = await stellarSdk.feeStats.execute(); + final fees = await (await stellarSdk).feeStats.execute(); return int.parse(fees.lastLedgerBaseFee); } - void _updateSdk() { + stellar.StellarSDK _getFreshSdk() { final currentNode = getCurrentNode(); HttpClient? _httpClient; - // TODO [prio=med]: refactor out and call before requests in case Tor is enabled/disabled, listen to prefs change, or similar. if (prefs.useTor) { final ({InternetAddress host, int port}) proxyInfo = TorService.sharedInstance.getProxyInfo(); @@ -73,8 +126,7 @@ class StellarWallet extends Bip39Wallet { ); } - _stellarSdk?.httpClient.close(); - _stellarSdk = stellar.StellarSDK( + return stellar.StellarSDK( "${currentNode.host}:${currentNode.port}", httpClient: _httpClient, ); @@ -84,7 +136,8 @@ class StellarWallet extends Bip39Wallet { bool exists = false; try { - final receiverAccount = await stellarSdk.accounts.account(accountId); + final receiverAccount = + await (await stellarSdk).accounts.account(accountId); if (receiverAccount.accountId != "") { exists = true; } @@ -191,7 +244,8 @@ class StellarWallet extends Bip39Wallet { @override Future confirmSend({required TxData txData}) async { final senderKeyPair = await _getSenderKeyPair(index: 0); - final sender = await stellarSdk.accounts.account(senderKeyPair.accountId); + final sender = + await (await stellarSdk).accounts.account(senderKeyPair.accountId); final address = txData.recipients!.first.address; final amountToSend = txData.recipients!.first.amount; @@ -229,7 +283,7 @@ class StellarWallet extends Bip39Wallet { transaction.sign(senderKeyPair, stellarNetwork); try { - final response = await stellarSdk.submitTransaction(transaction); + final response = await (await stellarSdk).submitTransaction(transaction); if (!response.success) { throw Exception("${response.extras?.resultCodes?.transactionResultCode}" " ::: ${response.extras?.resultCodes?.operationsResultCodes}"); @@ -256,7 +310,7 @@ class StellarWallet extends Bip39Wallet { @override Future get fees async { - int fee = await _getBaseFee(); + final int fee = await _getBaseFee(); return FeeObject( numberOfBlocksFast: 1, numberOfBlocksAverage: 1, @@ -294,7 +348,8 @@ class StellarWallet extends Bip39Wallet { stellar.AccountResponse accountResponse; try { - accountResponse = await stellarSdk.accounts + accountResponse = await (await stellarSdk) + .accounts .account((await getCurrentReceivingAddress())!.value) .onError((error, stackTrace) => throw error!); } catch (e) { @@ -315,7 +370,7 @@ class StellarWallet extends Bip39Wallet { } } - for (stellar.Balance balance in accountResponse.balances) { + for (final stellar.Balance balance in accountResponse.balances) { switch (balance.assetType) { case stellar.Asset.TYPE_NATIVE: final swBalance = Balance( @@ -352,7 +407,8 @@ class StellarWallet extends Bip39Wallet { @override Future updateChainHeight() async { try { - final height = await stellarSdk.ledgers + final height = await (await stellarSdk) + .ledgers .order(stellar.RequestBuilderOrder.DESC) .limit(1) .execute() @@ -370,7 +426,8 @@ class StellarWallet extends Bip39Wallet { @override Future updateNode() async { - _updateSdk(); + _stellarSdk?.httpClient.close(); + _stellarSdk = _getFreshSdk(); } @override @@ -378,10 +435,11 @@ class StellarWallet extends Bip39Wallet { try { final myAddress = (await getCurrentReceivingAddress())!; - List transactionList = []; + final List transactionList = []; stellar.Page payments; try { - payments = await stellarSdk.payments + payments = await (await stellarSdk) + .payments .forAccount(myAddress.value) .order(stellar.RequestBuilderOrder.DESC) .execute(); @@ -401,7 +459,7 @@ class StellarWallet extends Bip39Wallet { rethrow; } } - for (stellar.OperationResponse response in payments.records!) { + for (final stellar.OperationResponse response in payments.records!) { // PaymentOperationResponse por; if (response is stellar.PaymentOperationResponse) { final por = response; @@ -431,7 +489,8 @@ class StellarWallet extends Bip39Wallet { final List outputs = []; final List inputs = []; - OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor( + final OutputV2 output = + OutputV2.isarCantDoRequiredInDefaultConstructor( scriptPubKeyHex: "00", valueStringSats: amount.raw.toString(), addresses: [ @@ -439,7 +498,7 @@ class StellarWallet extends Bip39Wallet { ], walletOwns: addressTo == myAddress.value, ); - InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( + final InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( scriptSigHex: null, scriptSigAsm: null, sequence: null, @@ -459,8 +518,9 @@ class StellarWallet extends Bip39Wallet { int height = 0; //Query the transaction linked to the payment, // por.transaction returns a null sometimes - stellar.TransactionResponse tx = - await stellarSdk.transactions.transaction(por.transactionHash!); + final stellar.TransactionResponse tx = await (await stellarSdk) + .transactions + .transaction(por.transactionHash!); if (tx.hash.isNotEmpty) { fee = tx.feeCharged!; @@ -511,7 +571,8 @@ class StellarWallet extends Bip39Wallet { final List outputs = []; final List inputs = []; - OutputV2 output = OutputV2.isarCantDoRequiredInDefaultConstructor( + final OutputV2 output = + OutputV2.isarCantDoRequiredInDefaultConstructor( scriptPubKeyHex: "00", valueStringSats: amount.raw.toString(), addresses: [ @@ -520,7 +581,7 @@ class StellarWallet extends Bip39Wallet { ], walletOwns: caor.sourceAccount! == myAddress.value, ); - InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( + final InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor( scriptSigHex: null, scriptSigAsm: null, sequence: null, @@ -541,8 +602,9 @@ class StellarWallet extends Bip39Wallet { int fee = 0; int height = 0; - final tx = - await stellarSdk.transactions.transaction(caor.transactionHash!); + final tx = await (await stellarSdk) + .transactions + .transaction(caor.transactionHash!); if (tx.hash.isNotEmpty) { fee = tx.feeCharged!; height = tx.ledger;