From 5f4b35053f2c6775c5109d01e0ec6ff975286f32 Mon Sep 17 00:00:00 2001 From: Waleed Arshad <25613623+wal33d006@users.noreply.github.com> Date: Mon, 2 Aug 2021 19:51:40 +0500 Subject: [PATCH] feat: Transaction broadcasting (#72) * Add classes for transaction broadcasting * fix broadcasting compilation problems * Reformat transaction_hash.dart * Review PR suggestions Co-authored-by: Andrzej Chmielewski --- .../alan/alan_transaction_broadcaster.dart | 48 +++++++++++++++++++ .../gateway/transaction_signing_gateway.dart | 23 +++++++++ .../transaction_broadcasting_failure.dart | 20 ++++++++ .../lib/model/transaction_hash.dart | 5 ++ .../lib/transaction_broadcaster.dart | 24 ++++++++++ .../transaction_signing_gateway_test.dart | 1 + 6 files changed, 121 insertions(+) create mode 100644 packages/transaction_signing_gateway/lib/alan/alan_transaction_broadcaster.dart create mode 100644 packages/transaction_signing_gateway/lib/model/transaction_broadcasting_failure.dart create mode 100644 packages/transaction_signing_gateway/lib/model/transaction_hash.dart create mode 100644 packages/transaction_signing_gateway/lib/transaction_broadcaster.dart diff --git a/packages/transaction_signing_gateway/lib/alan/alan_transaction_broadcaster.dart b/packages/transaction_signing_gateway/lib/alan/alan_transaction_broadcaster.dart new file mode 100644 index 00000000..f2df1b7e --- /dev/null +++ b/packages/transaction_signing_gateway/lib/alan/alan_transaction_broadcaster.dart @@ -0,0 +1,48 @@ +import 'package:alan/transactions/sender/tx_sender.dart'; +import 'package:dartz/dartz.dart'; +import 'package:transaction_signing_gateway/alan/alan_private_wallet_credentials.dart'; +import 'package:transaction_signing_gateway/alan/alan_transaction.dart'; +import 'package:transaction_signing_gateway/model/private_wallet_credentials.dart'; +import 'package:transaction_signing_gateway/model/signed_transaction.dart'; +import 'package:transaction_signing_gateway/model/transaction_broadcasting_failure.dart'; +import 'package:transaction_signing_gateway/model/transaction_hash.dart'; +import 'package:transaction_signing_gateway/transaction_broadcaster.dart'; + +class AlanTransactionBroadcaster implements TransactionBroadcaster { + @override + Future> broadcast( + {required SignedTransaction transaction, required PrivateWalletCredentials privateWalletCredentials}) async { + if (transaction is! SignedAlanTransaction) { + return left(AlanTransactionBroadcastingFailure('passed transaction is not $SignedAlanTransaction')); + } + if (privateWalletCredentials is! AlanPrivateWalletCredentials) { + return left(AlanTransactionBroadcastingFailure("passed privateCredentials is not $AlanPrivateWalletCredentials")); + } + final txSender = TxSender.fromNetworkInfo(privateWalletCredentials.networkInfo); + final response = await txSender.broadcastTx(transaction.signedTransaction); + + if (response.hasTxhash()) { + return right(TransactionHash(txHash: response.txhash)); + } else { + return left(AlanTransactionBroadcastingFailure('Tx error: $response')); + } + } + + @override + bool canBroadcast(SignedTransaction signedTransaction) => signedTransaction is SignedAlanTransaction; +} + +class AlanTransactionBroadcastingFailure extends TransactionBroadcastingFailure { + final Object cause; + + AlanTransactionBroadcastingFailure(this.cause); + + @override + String toString() { + return 'AlanTransactionSigningFailure{cause: $cause}'; + } + + @override + // TODO: implement type + TransactionBroadcastingFailType get type => TransactionBroadcastingFailType.unknown; +} diff --git a/packages/transaction_signing_gateway/lib/gateway/transaction_signing_gateway.dart b/packages/transaction_signing_gateway/lib/gateway/transaction_signing_gateway.dart index fd9c8f21..f4beec65 100644 --- a/packages/transaction_signing_gateway/lib/gateway/transaction_signing_gateway.dart +++ b/packages/transaction_signing_gateway/lib/gateway/transaction_signing_gateway.dart @@ -3,24 +3,30 @@ import 'package:dartz/dartz.dart'; import 'package:transaction_signing_gateway/key_info_storage.dart'; import 'package:transaction_signing_gateway/model/credentials_storage_failure.dart'; import 'package:transaction_signing_gateway/model/signed_transaction.dart'; +import 'package:transaction_signing_gateway/model/transaction_broadcasting_failure.dart'; +import 'package:transaction_signing_gateway/model/transaction_hash.dart'; import 'package:transaction_signing_gateway/model/transaction_signing_failure.dart'; import 'package:transaction_signing_gateway/model/unsigned_transaction.dart'; import 'package:transaction_signing_gateway/model/wallet_lookup_key.dart'; import 'package:transaction_signing_gateway/model/wallet_public_info.dart'; +import 'package:transaction_signing_gateway/transaction_broadcaster.dart'; import 'package:transaction_signing_gateway/transaction_signer.dart'; import 'package:transaction_signing_gateway/transaction_signing_gateway.dart'; import 'package:transaction_signing_gateway/transaction_summary_ui.dart'; class TransactionSigningGateway { final List _signers; + final List _broadcasters; final KeyInfoStorage _infoStorage; final TransactionSummaryUI _transactionSummaryUI; TransactionSigningGateway({ required List signers, + required List broadcasters, required KeyInfoStorage infoStorage, required TransactionSummaryUI transactionSummaryUI, }) : _signers = List.unmodifiable(signers), + _broadcasters = List.unmodifiable(broadcasters), _infoStorage = infoStorage, _transactionSummaryUI = transactionSummaryUI; @@ -60,6 +66,18 @@ class TransactionSigningGateway { transaction: transaction, )); + Future> broadcastTransaction({ + required WalletLookupKey walletLookupKey, + required SignedTransaction transaction, + }) async => + _infoStorage + .getPrivateCredentials(walletLookupKey) + .leftMap((err) => left(StorageProblemBroadcastingFailure())) + .flatMap((privateCreds) async => _findCapableBroadcaster(transaction).broadcast( + transaction: transaction, + privateWalletCredentials: privateCreds, + )); + Future>> getWalletsList() => _infoStorage.getWalletsList(); /// Verifies if passed lookupKey is pointing to a valid wallet stored within the secure storage. @@ -70,4 +88,9 @@ class TransactionSigningGateway { (element) => element.canSign(transaction), orElse: () => NotFoundTransactionSigner(), ); + + TransactionBroadcaster _findCapableBroadcaster(SignedTransaction transaction) => _broadcasters.firstWhere( + (element) => element.canBroadcast(transaction), + orElse: () => NotFoundBroadcaster(), + ); } diff --git a/packages/transaction_signing_gateway/lib/model/transaction_broadcasting_failure.dart b/packages/transaction_signing_gateway/lib/model/transaction_broadcasting_failure.dart new file mode 100644 index 00000000..b0fc0281 --- /dev/null +++ b/packages/transaction_signing_gateway/lib/model/transaction_broadcasting_failure.dart @@ -0,0 +1,20 @@ +enum TransactionBroadcastingFailType { + noTransactionBroadcasterFound, + walletCredentialsStorageFailure, + unknown, +} + +abstract class TransactionBroadcastingFailure { + TransactionBroadcastingFailType get type; +} + +class TransactionBroadcasterNotFoundFailure extends TransactionBroadcastingFailure { + @override + TransactionBroadcastingFailType get type => TransactionBroadcastingFailType.noTransactionBroadcasterFound; +} + +class StorageProblemBroadcastingFailure extends TransactionBroadcastingFailure { + @override + // TODO: implement type + TransactionBroadcastingFailType get type => TransactionBroadcastingFailType.walletCredentialsStorageFailure; +} diff --git a/packages/transaction_signing_gateway/lib/model/transaction_hash.dart b/packages/transaction_signing_gateway/lib/model/transaction_hash.dart new file mode 100644 index 00000000..7ec1a593 --- /dev/null +++ b/packages/transaction_signing_gateway/lib/model/transaction_hash.dart @@ -0,0 +1,5 @@ +class TransactionHash { + String txHash; + + TransactionHash({required this.txHash}); +} diff --git a/packages/transaction_signing_gateway/lib/transaction_broadcaster.dart b/packages/transaction_signing_gateway/lib/transaction_broadcaster.dart new file mode 100644 index 00000000..ab03b618 --- /dev/null +++ b/packages/transaction_signing_gateway/lib/transaction_broadcaster.dart @@ -0,0 +1,24 @@ +import 'package:dartz/dartz.dart'; +import 'package:transaction_signing_gateway/model/private_wallet_credentials.dart'; +import 'package:transaction_signing_gateway/model/signed_transaction.dart'; +import 'package:transaction_signing_gateway/model/transaction_broadcasting_failure.dart'; +import 'package:transaction_signing_gateway/model/transaction_hash.dart'; + +abstract class TransactionBroadcaster { + Future> broadcast({ + required SignedTransaction transaction, + required PrivateWalletCredentials privateWalletCredentials, + }); + + bool canBroadcast(SignedTransaction signedTransaction); +} + +class NotFoundBroadcaster implements TransactionBroadcaster { + @override + Future> broadcast( + {required SignedTransaction transaction, required PrivateWalletCredentials privateWalletCredentials}) async => + left(TransactionBroadcasterNotFoundFailure()); + + @override + bool canBroadcast(SignedTransaction signedTransaction) => true; +} diff --git a/packages/transaction_signing_gateway/test/transaction_signing_gateway_test.dart b/packages/transaction_signing_gateway/test/transaction_signing_gateway_test.dart index ce0a38f3..34837fc5 100644 --- a/packages/transaction_signing_gateway/test/transaction_signing_gateway_test.dart +++ b/packages/transaction_signing_gateway/test/transaction_signing_gateway_test.dart @@ -98,6 +98,7 @@ void main() { signingGateway = TransactionSigningGateway( transactionSummaryUI: summaryUI, signers: [], + broadcasters: [], infoStorage: infoStorage, ); });