From 15a55f1415253d2d8b281735e0c77e486f1b0f99 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 13 Sep 2024 15:43:44 +0800 Subject: [PATCH 1/5] Add new type MultiTransaction and new web api --- protobuf/aelf/core.proto | 10 ++++ src/AElf.Types/AElf.Types.csproj | 1 + src/AElf.Types/Types/MultiTransaction.cs | 52 +++++++++++++++++++ .../Dto/SendMultiTransactionInput.cs | 6 +++ .../Dto/SendMultiTransactionOutput.cs | 6 +++ src/AElf.WebApp.Application.Chain/Error.cs | 2 + .../MultiTransactionSignerOptions.cs | 6 +++ .../Services/TransactionAppService.cs | 38 +++++++++++++- 8 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/AElf.Types/Types/MultiTransaction.cs create mode 100644 src/AElf.WebApp.Application.Chain/Dto/SendMultiTransactionInput.cs create mode 100644 src/AElf.WebApp.Application.Chain/Dto/SendMultiTransactionOutput.cs create mode 100644 src/AElf.WebApp.Application.Chain/MultiTransactionSignerOptions.cs diff --git a/protobuf/aelf/core.proto b/protobuf/aelf/core.proto index e45d7e581d..4fe3a5bcf4 100644 --- a/protobuf/aelf/core.proto +++ b/protobuf/aelf/core.proto @@ -22,6 +22,16 @@ message Transaction { bytes signature = 10000; } +message TransactionAndChainId { + Transaction transaction = 1; + int32 chain_id = 2; +} + +message MultiTransaction { + repeated TransactionAndChainId transactions = 1; + bytes signature = 10000; +} + message StatePath { // The partial path of the state path. repeated string parts = 1; diff --git a/src/AElf.Types/AElf.Types.csproj b/src/AElf.Types/AElf.Types.csproj index 867163cd35..8698f88f84 100644 --- a/src/AElf.Types/AElf.Types.csproj +++ b/src/AElf.Types/AElf.Types.csproj @@ -9,6 +9,7 @@ true true true + 1.10.1 diff --git a/src/AElf.Types/Types/MultiTransaction.cs b/src/AElf.Types/Types/MultiTransaction.cs new file mode 100644 index 0000000000..c1ba07f146 --- /dev/null +++ b/src/AElf.Types/Types/MultiTransaction.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using Google.Protobuf; + +namespace AElf.Types +{ + public partial class MultiTransaction + { + private Hash _transactionId; + + public Hash GetHash() + { + if (_transactionId == null) + _transactionId = HashHelper.ComputeFrom(GetSignatureData()); + + return _transactionId; + } + + public bool VerifyFields() + { + if (Transactions.Count < 2) + return false; + + if (!AllTransactionsHaveSameFrom()) + return false; + + if (Transactions.Any(transaction => string.IsNullOrEmpty(transaction.Transaction.MethodName))) + return false; + + return true; + } + + private bool AllTransactionsHaveSameFrom() + { + var firstFrom = Transactions[0].Transaction.From; + return Transactions.All(transaction => transaction.Transaction.From == firstFrom); + } + + private byte[] GetSignatureData() + { + if (!VerifyFields()) + throw new InvalidOperationException($"Invalid x transaction: {this}"); + + if (Signature.IsEmpty) + return this.ToByteArray(); + + var transaction = Clone(); + transaction.Signature = ByteString.Empty; + return transaction.ToByteArray(); + } + } +} \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Dto/SendMultiTransactionInput.cs b/src/AElf.WebApp.Application.Chain/Dto/SendMultiTransactionInput.cs new file mode 100644 index 0000000000..4a0afff9e1 --- /dev/null +++ b/src/AElf.WebApp.Application.Chain/Dto/SendMultiTransactionInput.cs @@ -0,0 +1,6 @@ +namespace AElf.WebApp.Application.Chain.Dto; + +public class SendMultiTransactionInput : SendTransactionsInput +{ + +} \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Dto/SendMultiTransactionOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/SendMultiTransactionOutput.cs new file mode 100644 index 0000000000..3fcc80a61f --- /dev/null +++ b/src/AElf.WebApp.Application.Chain/Dto/SendMultiTransactionOutput.cs @@ -0,0 +1,6 @@ +namespace AElf.WebApp.Application.Chain.Dto; + +public class SendMultiTransactionOutput +{ + public string[] TransactionIds { get; set; } +} diff --git a/src/AElf.WebApp.Application.Chain/Error.cs b/src/AElf.WebApp.Application.Chain/Error.cs index e194dacbad..91dc2f40d8 100644 --- a/src/AElf.WebApp.Application.Chain/Error.cs +++ b/src/AElf.WebApp.Application.Chain/Error.cs @@ -11,6 +11,7 @@ public static class Error public const int InvalidOffset = 20006; public const int InvalidLimit = 20007; public const int InvalidTransaction = 20008; + public const int InvalidXTransaction = 20009; public const int InvalidContractAddress = 20010; public const int NoMatchMethodInContractAddress = 20011; public const int InvalidParams = 20012; @@ -26,6 +27,7 @@ public static class Error { InvalidOffset, "Offset must greater than or equal to 0" }, { InvalidLimit, "Limit must between 0 and 100" }, { InvalidTransaction, "Invalid transaction information" }, + { InvalidXTransaction, "Invalid multi-transaction information" }, { InvalidContractAddress, "Invalid contract address" }, { NoMatchMethodInContractAddress, "No match method in contract address" }, { InvalidParams, "Invalid params" }, diff --git a/src/AElf.WebApp.Application.Chain/MultiTransactionSignerOptions.cs b/src/AElf.WebApp.Application.Chain/MultiTransactionSignerOptions.cs new file mode 100644 index 0000000000..ea0f9dcb1b --- /dev/null +++ b/src/AElf.WebApp.Application.Chain/MultiTransactionSignerOptions.cs @@ -0,0 +1,6 @@ +namespace AElf.WebApp.Application.Chain; + +public class MultiTransactionSignerOptions +{ + public string Address { get; set; } +} \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index f4836f8128..6ced84c81e 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using AElf.Cryptography; using AElf.Kernel; using AElf.Kernel.Blockchain.Application; using AElf.Kernel.FeeCalculation.Extensions; @@ -36,6 +37,8 @@ public interface ITransactionAppService Task SendTransactionAsync(SendTransactionInput input); + Task SendMultiTransactionAsync(SendMultiTransactionInput input); + Task SendTransactionsAsync(SendTransactionsInput input); Task CalculateTransactionFeeAsync(CalculateTransactionFeeInput input); @@ -49,19 +52,21 @@ public class TransactionAppService : AElfAppService, ITransactionAppService private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; private readonly IPlainTransactionExecutingService _plainTransactionExecutingService; private readonly WebAppOptions _webAppOptions; - + private readonly MultiTransactionSignerOptions _multiTransactionSignerOptions; public TransactionAppService(ITransactionReadOnlyExecutionService transactionReadOnlyExecutionService, IBlockchainService blockchainService, IObjectMapper objectMapper, ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider, IPlainTransactionExecutingService plainTransactionExecutingService, - IOptionsMonitor webAppOptions) + IOptionsMonitor webAppOptions, + IOptionsSnapshot multiTransactionSignerOptions) { _transactionReadOnlyExecutionService = transactionReadOnlyExecutionService; _blockchainService = blockchainService; _objectMapper = objectMapper; _transactionResultStatusCacheProvider = transactionResultStatusCacheProvider; _plainTransactionExecutingService = plainTransactionExecutingService; + _multiTransactionSignerOptions = multiTransactionSignerOptions.Value; _webAppOptions = webAppOptions.CurrentValue; LocalEventBus = NullLocalEventBus.Instance; @@ -238,6 +243,35 @@ public async Task SendTransactionAsync(SendTransactionInp }; } + public async Task SendMultiTransactionAsync(SendMultiTransactionInput input) + { + var xTxBytes = ByteArrayHelper.HexStringToByteArray(input.RawTransactions); + var xTx = MultiTransaction.Parser.ParseFrom(xTxBytes); + if (!xTx.VerifyFields()) + { + throw new UserFriendlyException(Error.Message[Error.InvalidTransaction], + Error.InvalidTransaction.ToString()); + } + + CryptoHelper.RecoverPublicKey(xTx.Signature.ToByteArray(), xTx.GetHash().ToByteArray(), out var pubkey); + if (Address.FromPublicKey(pubkey).ToBase58() != _multiTransactionSignerOptions.Address) + { + throw new UserFriendlyException(Error.Message[Error.InvalidSignature], + Error.InvalidSignature.ToString()); + } + + var chain = await _blockchainService.GetChainAsync(); + var txListOfCurrentChain = xTx.Transactions + .Where(t => t.ChainId == chain.Id) + .Select(t => t.Transaction.ToByteArray().ToHex()).ToArray(); + var txIds = await PublishTransactionsAsync(txListOfCurrentChain); + + return new SendMultiTransactionOutput + { + TransactionIds = txIds + }; + } + /// /// Broadcast multiple transactions /// From f5044a533c84d8d7e20c554b7493f9626eba05cb Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 19 Sep 2024 11:14:27 +0800 Subject: [PATCH 2/5] Tune MultiTransactionOptions --- src/AElf.Types/AElf.Types.csproj | 1 - .../ChainApplicationWebAppAElfModule.cs | 3 +++ src/AElf.WebApp.Application.Chain/Error.cs | 4 +++- .../MultiTransactionOptions.cs | 7 +++++++ .../MultiTransactionSignerOptions.cs | 6 ------ .../Services/TransactionAppService.cs | 15 +++++++++++---- 6 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 src/AElf.WebApp.Application.Chain/MultiTransactionOptions.cs delete mode 100644 src/AElf.WebApp.Application.Chain/MultiTransactionSignerOptions.cs diff --git a/src/AElf.Types/AElf.Types.csproj b/src/AElf.Types/AElf.Types.csproj index 8698f88f84..867163cd35 100644 --- a/src/AElf.Types/AElf.Types.csproj +++ b/src/AElf.Types/AElf.Types.csproj @@ -9,7 +9,6 @@ true true true - 1.10.1 diff --git a/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs b/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs index d5820e1828..7455e1a9ad 100644 --- a/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs +++ b/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs @@ -22,5 +22,8 @@ public override void ConfigureServices(ServiceConfigurationContext context) context.Services .AddSingleton(); + + Configure(context.Services.GetConfiguration() + .GetSection("MultiTransaction")); } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Error.cs b/src/AElf.WebApp.Application.Chain/Error.cs index 91dc2f40d8..f1782f4f9b 100644 --- a/src/AElf.WebApp.Application.Chain/Error.cs +++ b/src/AElf.WebApp.Application.Chain/Error.cs @@ -16,6 +16,7 @@ public static class Error public const int NoMatchMethodInContractAddress = 20011; public const int InvalidParams = 20012; public const int InvalidSignature = 20013; + public const int InvalidGatewaySignature = 20014; public const string NeedBasicAuth = "User name and password for basic auth should be set"; public static readonly Dictionary Message = new() @@ -31,6 +32,7 @@ public static class Error { InvalidContractAddress, "Invalid contract address" }, { NoMatchMethodInContractAddress, "No match method in contract address" }, { InvalidParams, "Invalid params" }, - { InvalidSignature, "Invalid signature" } + { InvalidSignature, "Invalid signature" }, + { InvalidGatewaySignature, "Invalid gateway signature" } }; } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/MultiTransactionOptions.cs b/src/AElf.WebApp.Application.Chain/MultiTransactionOptions.cs new file mode 100644 index 0000000000..076fe4502c --- /dev/null +++ b/src/AElf.WebApp.Application.Chain/MultiTransactionOptions.cs @@ -0,0 +1,7 @@ +namespace AElf.WebApp.Application.Chain; + +public class MultiTransactionOptions +{ + public string GatewayAddress { get; set; } + public string GatewayContractAddress { get; set; } +} \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/MultiTransactionSignerOptions.cs b/src/AElf.WebApp.Application.Chain/MultiTransactionSignerOptions.cs deleted file mode 100644 index ea0f9dcb1b..0000000000 --- a/src/AElf.WebApp.Application.Chain/MultiTransactionSignerOptions.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace AElf.WebApp.Application.Chain; - -public class MultiTransactionSignerOptions -{ - public string Address { get; set; } -} \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index 6ced84c81e..2ba8f8a504 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -52,21 +52,21 @@ public class TransactionAppService : AElfAppService, ITransactionAppService private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; private readonly IPlainTransactionExecutingService _plainTransactionExecutingService; private readonly WebAppOptions _webAppOptions; - private readonly MultiTransactionSignerOptions _multiTransactionSignerOptions; + private readonly MultiTransactionOptions _multiTransactionOptions; public TransactionAppService(ITransactionReadOnlyExecutionService transactionReadOnlyExecutionService, IBlockchainService blockchainService, IObjectMapper objectMapper, ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider, IPlainTransactionExecutingService plainTransactionExecutingService, IOptionsMonitor webAppOptions, - IOptionsSnapshot multiTransactionSignerOptions) + IOptionsSnapshot multiTransactionSignerOptions) { _transactionReadOnlyExecutionService = transactionReadOnlyExecutionService; _blockchainService = blockchainService; _objectMapper = objectMapper; _transactionResultStatusCacheProvider = transactionResultStatusCacheProvider; _plainTransactionExecutingService = plainTransactionExecutingService; - _multiTransactionSignerOptions = multiTransactionSignerOptions.Value; + _multiTransactionOptions = multiTransactionSignerOptions.Value; _webAppOptions = webAppOptions.CurrentValue; LocalEventBus = NullLocalEventBus.Instance; @@ -254,7 +254,8 @@ public async Task SendMultiTransactionAsync(SendMult } CryptoHelper.RecoverPublicKey(xTx.Signature.ToByteArray(), xTx.GetHash().ToByteArray(), out var pubkey); - if (Address.FromPublicKey(pubkey).ToBase58() != _multiTransactionSignerOptions.Address) + + if (!IsGatewayAddress(Address.FromPublicKey(pubkey))) { throw new UserFriendlyException(Error.Message[Error.InvalidSignature], Error.InvalidSignature.ToString()); @@ -272,6 +273,12 @@ public async Task SendMultiTransactionAsync(SendMult }; } + private bool IsGatewayAddress(Address address) + { + // TODO: Execute IsGatewayAddress method on MultiTxGateway contract + return _multiTransactionOptions.GatewayAddress == address.ToBase58(); + } + /// /// Broadcast multiple transactions /// From 9eddb64c3df0bff0509fcdba5d50302efef362a6 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 19 Sep 2024 14:25:45 +0800 Subject: [PATCH 3/5] Improve IsGatewayAddress --- .../Services/TransactionAppService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index 2ba8f8a504..1f466be7e1 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -275,6 +275,11 @@ public async Task SendMultiTransactionAsync(SendMult private bool IsGatewayAddress(Address address) { + if (string.IsNullOrEmpty(_multiTransactionOptions.GatewayAddress)) + { + return true; + } + // TODO: Execute IsGatewayAddress method on MultiTxGateway contract return _multiTransactionOptions.GatewayAddress == address.ToBase58(); } From 276642a152a1857ef1698a5986ad8642a2cf423c Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 20 Sep 2024 11:43:49 +0800 Subject: [PATCH 4/5] Update IsGatewayAddress impl --- .../Services/TransactionAppService.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index 1f466be7e1..bc95246b51 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -255,10 +255,10 @@ public async Task SendMultiTransactionAsync(SendMult CryptoHelper.RecoverPublicKey(xTx.Signature.ToByteArray(), xTx.GetHash().ToByteArray(), out var pubkey); - if (!IsGatewayAddress(Address.FromPublicKey(pubkey))) + if (!await IsGatewayAddress(Address.FromPublicKey(pubkey))) { - throw new UserFriendlyException(Error.Message[Error.InvalidSignature], - Error.InvalidSignature.ToString()); + throw new UserFriendlyException(Error.Message[Error.InvalidGatewaySignature], + Error.InvalidGatewaySignature.ToString()); } var chain = await _blockchainService.GetChainAsync(); @@ -273,14 +273,31 @@ public async Task SendMultiTransactionAsync(SendMult }; } - private bool IsGatewayAddress(Address address) + private async Task IsGatewayAddress(Address address) { - if (string.IsNullOrEmpty(_multiTransactionOptions.GatewayAddress)) + if (string.IsNullOrEmpty(_multiTransactionOptions.GatewayAddress) && + string.IsNullOrEmpty(_multiTransactionOptions.GatewayContractAddress)) { return true; } - // TODO: Execute IsGatewayAddress method on MultiTxGateway contract + if (!string.IsNullOrEmpty(_multiTransactionOptions.GatewayContractAddress)) + { + var chain = await _blockchainService.GetChainAsync(); + var isGatewayAddressBytes = await CallReadOnlyAsync(new Transaction + { + From = address, + To = Address.FromBase58(_multiTransactionOptions.GatewayContractAddress), + MethodName = "IsGatewayAddress", + Params = address.ToByteString(), + RefBlockNumber = chain.BestChainHeight, + RefBlockPrefix = BlockHelper.GetRefBlockPrefix(chain.BestChainHash) + }); + var isGatewayAddress = new BoolValue(); + isGatewayAddress.MergeFrom(isGatewayAddressBytes); + return isGatewayAddress.Value; + } + return _multiTransactionOptions.GatewayAddress == address.ToBase58(); } From 6f0a5b03a8e1b96535de42b6f0831f1a0d66f606 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Sun, 29 Sep 2024 14:07:25 +0800 Subject: [PATCH 5/5] Improve multi transaction verification. --- src/AElf.Types/Types/MultiTransaction.cs | 35 +++++++++++++------ .../Services/TransactionAppService.cs | 10 +++--- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/AElf.Types/Types/MultiTransaction.cs b/src/AElf.Types/Types/MultiTransaction.cs index c1ba07f146..e45c087ae7 100644 --- a/src/AElf.Types/Types/MultiTransaction.cs +++ b/src/AElf.Types/Types/MultiTransaction.cs @@ -16,18 +16,32 @@ public Hash GetHash() return _transactionId; } - public bool VerifyFields() + public ValidationStatus VerifyFields() { if (Transactions.Count < 2) - return false; + return ValidationStatus.OnlyOneTransaction; if (!AllTransactionsHaveSameFrom()) - return false; + return ValidationStatus.MoreThanOneFrom; if (Transactions.Any(transaction => string.IsNullOrEmpty(transaction.Transaction.MethodName))) - return false; + return ValidationStatus.MethodNameIsEmpty; - return true; + if (Transactions.Any(transaction => transaction.Transaction.Signature.IsEmpty)) + { + return ValidationStatus.UserSignatureIsEmpty; + } + + return ValidationStatus.Success; + } + + public enum ValidationStatus + { + Success, + OnlyOneTransaction, + MoreThanOneFrom, + MethodNameIsEmpty, + UserSignatureIsEmpty } private bool AllTransactionsHaveSameFrom() @@ -38,15 +52,16 @@ private bool AllTransactionsHaveSameFrom() private byte[] GetSignatureData() { - if (!VerifyFields()) - throw new InvalidOperationException($"Invalid x transaction: {this}"); + var verifyResult = VerifyFields(); + if (verifyResult != ValidationStatus.Success) + throw new InvalidOperationException($"Invalid multi transaction, {verifyResult.ToString()}: {this}"); if (Signature.IsEmpty) return this.ToByteArray(); - var transaction = Clone(); - transaction.Signature = ByteString.Empty; - return transaction.ToByteArray(); + var multiTransaction = Clone(); + multiTransaction.Signature = ByteString.Empty; + return multiTransaction.ToByteArray(); } } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index bc95246b51..1fe31f00a6 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -245,15 +245,15 @@ public async Task SendTransactionAsync(SendTransactionInp public async Task SendMultiTransactionAsync(SendMultiTransactionInput input) { - var xTxBytes = ByteArrayHelper.HexStringToByteArray(input.RawTransactions); - var xTx = MultiTransaction.Parser.ParseFrom(xTxBytes); - if (!xTx.VerifyFields()) + var multiTxBytes = ByteArrayHelper.HexStringToByteArray(input.RawTransactions); + var multiTransaction = MultiTransaction.Parser.ParseFrom(multiTxBytes); + if (multiTransaction.VerifyFields() != MultiTransaction.ValidationStatus.Success) { throw new UserFriendlyException(Error.Message[Error.InvalidTransaction], Error.InvalidTransaction.ToString()); } - CryptoHelper.RecoverPublicKey(xTx.Signature.ToByteArray(), xTx.GetHash().ToByteArray(), out var pubkey); + CryptoHelper.RecoverPublicKey(multiTransaction.Signature.ToByteArray(), multiTransaction.GetHash().ToByteArray(), out var pubkey); if (!await IsGatewayAddress(Address.FromPublicKey(pubkey))) { @@ -262,7 +262,7 @@ public async Task SendMultiTransactionAsync(SendMult } var chain = await _blockchainService.GetChainAsync(); - var txListOfCurrentChain = xTx.Transactions + var txListOfCurrentChain = multiTransaction.Transactions .Where(t => t.ChainId == chain.Id) .Select(t => t.Transaction.ToByteArray().ToHex()).ToArray(); var txIds = await PublishTransactionsAsync(txListOfCurrentChain);