diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs index da1f9dc2b34f..42bd703391d3 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs @@ -1,9 +1,8 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -// using System.Collections.Generic; - using System.Collections.Generic; +using System.Threading.Tasks; using FluentAssertions; using Nethermind.Core; using Nethermind.Core.Eip2930; @@ -20,7 +19,7 @@ namespace Nethermind.JsonRpc.Test.Modules; public class TxPoolRpcModuleTests { [Test] - public void Pool_content_produces_transactions_with_ChainId() + public void TxPoolContent_WhenLegacyTxHasNoChainId_SerializesChainIdFromSpec() { const ulong SomeChainId = 123ul; Transaction txA = Build.A.Transaction @@ -62,10 +61,267 @@ public void Pool_content_produces_transactions_with_ChainId() TxPoolContent txpoolContent = txPoolRpcModule.txpool_content().Data; - LegacyTransactionForRpc? rpcTxA = txpoolContent.Pending[new AddressAsKey(TestItem.AddressA)][1] as LegacyTransactionForRpc; - AccessListTransactionForRpc? rpcTxB = txpoolContent.Queued[new AddressAsKey(TestItem.AddressB)][2] as AccessListTransactionForRpc; + LegacyTransactionForRpc? rpcTxA = txpoolContent.Pending[TestItem.AddressA.ToString(withZeroX: true, withEip55Checksum: true)][1] as LegacyTransactionForRpc; + AccessListTransactionForRpc? rpcTxB = txpoolContent.Queued[TestItem.AddressB.ToString(withZeroX: true, withEip55Checksum: true)][2] as AccessListTransactionForRpc; + + rpcTxA!.ChainId.Should().BeNull("legacy txs without chainId must not have one injected"); + rpcTxB!.ChainId.Should().Be(SomeChainId, "EIP-2930 txs without chainId should inherit it from the spec provider"); + } + + [Test] + public void TxPoolStatus_WhenPoolHasTransactions_ReturnsPendingAndQueuedCounts() + { + Transaction txA = Build.A.Transaction.WithType(TxType.Legacy).TestObject; + Transaction txB = Build.A.Transaction.WithType(TxType.Legacy).TestObject; + + ITxPoolInfoProvider txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new() + { + { + new AddressAsKey(TestItem.AddressA), new Dictionary + { + { 1, txA }, { 2, txB } + } + } + }, + queued: new() + { + { + new AddressAsKey(TestItem.AddressB), new Dictionary + { + { 5, txA } + } + } + } + )); + + ISpecProvider specProvider = Substitute.For(); + TxPoolRpcModule txPoolRpcModule = new(txPoolInfoProvider, specProvider); + + TxPoolStatus status = txPoolRpcModule.txpool_status().Data; + + status.Pending.Should().Be(2ul, "AddressA has 2 pending transactions"); + status.Queued.Should().Be(1ul, "AddressB has 1 queued transaction"); + } + + [Test] + public void TxPoolContentFrom_WhenAddressIsInPool_ReturnsOnlyMatchingTransactions() + { + // AddressA has nonces 1 and 2 in pending; AddressB has nonce 3. + // Querying for AddressA must return only its 2 transactions and an empty queued map. + const ulong SomeChainId = 1ul; + Transaction txA = Build.A.Transaction.WithType(TxType.Legacy).WithChainId(null).TestObject; + Transaction txB = Build.A.Transaction.WithType(TxType.Legacy).WithChainId(null).TestObject; + Transaction txC = Build.A.Transaction.WithType(TxType.Legacy).WithChainId(null).TestObject; + + ITxPoolInfoProvider txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new() + { + { + new AddressAsKey(TestItem.AddressA), new Dictionary + { + { 1, txA }, { 2, txB } + } + }, + { + new AddressAsKey(TestItem.AddressB), new Dictionary + { + { 3, txC } + } + } + }, + queued: new() + )); + + ISpecProvider specProvider = Substitute.For(); + specProvider.ChainId.Returns(SomeChainId); + + TxPoolRpcModule txPoolRpcModule = new(txPoolInfoProvider, specProvider); + + TxPoolContentFrom result = txPoolRpcModule.txpool_contentFrom(TestItem.AddressA).Data; + + result.Pending.Should().HaveCount(2, "AddressA has exactly 2 pending transactions"); + result.Pending.Should().ContainKey(1ul, "nonce 1 belongs to AddressA"); + result.Pending.Should().ContainKey(2ul, "nonce 2 belongs to AddressA"); + result.Queued.Should().BeEmpty("no queued transactions were set up for AddressA"); + } + + [Test] + public void TxPoolContentFrom_WhenAddressNotInPool_ReturnsEmptyPendingAndQueued() + { + ITxPoolInfoProvider txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new(), + queued: new() + )); + + ISpecProvider specProvider = Substitute.For(); + specProvider.ChainId.Returns(1ul); + + TxPoolRpcModule txPoolRpcModule = new(txPoolInfoProvider, specProvider); + + TxPoolContentFrom result = txPoolRpcModule.txpool_contentFrom(TestItem.AddressA).Data; + + result.Pending.Should().BeEmpty("the pool has no transactions for any address"); + result.Queued.Should().BeEmpty("the pool has no transactions for any address"); + } + + [Test] + public async Task TxPoolStatus_WhenPoolHasTransactions_SerializesCountsAsHexStrings() + { + Transaction txA = Build.A.Transaction.WithType(TxType.Legacy).TestObject; + Transaction txB = Build.A.Transaction.WithType(TxType.Legacy).TestObject; + + ITxPoolInfoProvider txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new() + { + { + new AddressAsKey(TestItem.AddressA), new Dictionary + { + { 1, txA }, { 2, txB } + } + } + }, + queued: new() + { + { + new AddressAsKey(TestItem.AddressB), new Dictionary + { + { 5, txA } + } + } + } + )); + + ISpecProvider specProvider = Substitute.For(); + TxPoolRpcModule txPoolRpcModule = new(txPoolInfoProvider, specProvider); + + string json = await RpcTest.TestSerializedRequest(txPoolRpcModule, "txpool_status"); + + json.Should().Contain("\"pending\":\"0x2\"", "the spec requires pending count as a hex-encoded uint"); + json.Should().Contain("\"queued\":\"0x1\"", "the spec requires queued count as a hex-encoded uint"); + } + + [Test] + public async Task TxPoolContent_WhenNonceIsLarge_SerializesNonceKeyAsDecimalString() + { + Transaction tx = Build.A.Transaction + .WithType(TxType.Legacy) + .WithNonce(806) + .WithSenderAddress(TestItem.AddressA) + .TestObject; + + ITxPoolInfoProvider txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new() + { + { + new AddressAsKey(TestItem.AddressA), new Dictionary + { + { 806, tx } + } + } + }, + queued: new() + )); + + ISpecProvider specProvider = Substitute.For(); + specProvider.ChainId.Returns(1ul); + TxPoolRpcModule txPoolRpcModule = new(txPoolInfoProvider, specProvider); + + string json = await RpcTest.TestSerializedRequest(txPoolRpcModule, "txpool_content"); + + json.Should().Contain("\"806\":{", "the spec requires nonce map keys as decimal strings, not hex"); + json.Should().NotContain("\"0x326\":{", "hex nonce keys would violate the spec"); + } + + [Test] + public async Task TxPoolContent_WhenTransactionIsPending_IncludesNullBlockFieldsAndFrom() + { + Transaction tx = Build.A.Transaction + .WithType(TxType.Legacy) + .WithSenderAddress(TestItem.AddressA) + .TestObject; + + ITxPoolInfoProvider txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new() + { + { + new AddressAsKey(TestItem.AddressA), new Dictionary + { + { 0, tx } + } + } + }, + queued: new() + )); + + ISpecProvider specProvider = Substitute.For(); + specProvider.ChainId.Returns(1ul); + TxPoolRpcModule txPoolRpcModule = new(txPoolInfoProvider, specProvider); + + string json = await RpcTest.TestSerializedRequest(txPoolRpcModule, "txpool_content"); + + json.Should().Contain("\"blockHash\":null", "pending transactions have no block context"); + json.Should().Contain("\"blockNumber\":null", "pending transactions have no block context"); + json.Should().Contain("\"blockTimestamp\":null", "pending transactions have no block context"); + json.Should().Contain("\"transactionIndex\":null", "pending transactions have no block context"); + json.Should().Contain("\"from\":\"" + TestItem.AddressA.ToString().ToLowerInvariant() + "\"", + "the spec requires 'from' to be present on every pending transaction"); + json.Should().Contain("\"" + TestItem.AddressA.ToString(withZeroX: true, withEip55Checksum: true) + "\":{", + "address map keys must use EIP-55 checksum format to match the spec"); + } + + [Test] + public async Task TxPoolContentFrom_WhenNonceIsLarge_SerializesNonceKeyAsDecimalString() + { + Transaction tx = Build.A.Transaction + .WithType(TxType.Legacy) + .WithNonce(806) + .WithSenderAddress(TestItem.AddressA) + .TestObject; + + ITxPoolInfoProvider txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new() + { + { + new AddressAsKey(TestItem.AddressA), new Dictionary + { + { 806, tx } + } + } + }, + queued: new() + )); + + ISpecProvider specProvider = Substitute.For(); + specProvider.ChainId.Returns(1ul); + TxPoolRpcModule txPoolRpcModule = new(txPoolInfoProvider, specProvider); + + string json = await RpcTest.TestSerializedRequest(txPoolRpcModule, "txpool_contentFrom", TestItem.AddressA); + + json.Should().Contain("\"806\":{", "the spec requires nonce map keys as decimal strings, not hex"); + json.Should().NotContain("\"0x326\":{", "hex nonce keys would violate the spec"); + } + + [Test] + public async Task TxPoolContentFrom_WhenAddressNotInPool_SerializesEmptyPendingAndQueued() + { + ITxPoolInfoProvider txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo(pending: new(), queued: new())); + + ISpecProvider specProvider = Substitute.For(); + specProvider.ChainId.Returns(1ul); + TxPoolRpcModule txPoolRpcModule = new(txPoolInfoProvider, specProvider); + + string json = await RpcTest.TestSerializedRequest(txPoolRpcModule, "txpool_contentFrom", TestItem.AddressA); - rpcTxA!.ChainId.Should().BeNull(); - rpcTxB!.ChainId.Should().Be(SomeChainId); + json.Should().Contain("\"pending\":{}", "spec requires the pending field to always be present"); + json.Should().Contain("\"queued\":{}", "spec requires the queued field to always be present"); } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/ITxPoolRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/ITxPoolRpcModule.cs index 42794cbc0c5f..a96f24b80ad8 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/ITxPoolRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/ITxPoolRpcModule.cs @@ -1,22 +1,28 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -namespace Nethermind.JsonRpc.Modules.TxPool +using Nethermind.Core; + +namespace Nethermind.JsonRpc.Modules.TxPool; + +[RpcModule(ModuleType.TxPool)] +public interface ITxPoolRpcModule : IRpcModule { - [RpcModule(ModuleType.TxPool)] - public interface ITxPoolRpcModule : IRpcModule - { - [JsonRpcMethod(Description = "Returns a tx pool status.", IsImplemented = true, ExampleResponse = "{\"pending\":1010,\"queued\":14}")] - ResultWrapper txpool_status(); + [JsonRpcMethod(Description = "Returns a tx pool status.", IsImplemented = true, ExampleResponse = "{\"pending\":\"0x3f2\",\"queued\":\"0xe\"}")] + ResultWrapper txpool_status(); + + [JsonRpcMethod(Description = "Returns tx pool content.", + IsImplemented = true, + ExampleResponse = "{\"0x0f990ef7ec160f01af7148b74cc8a86fe46c551e\":{\"153\":{\"hash\":\"0x84f6f2e5d24b9a0c25bd7018adbbf4388b2c07842782f73d5ddc389906d5f2c8\",\"nonce\":\"0x99\",\"blockHash\":null,\"blockNumber\":null,\"blockTimestamp\":null,\"transactionIndex\":null,\"from\":\"0x0f990ef7ec160f01af7148b74cc8a86fe46c551e\",\"to\":\"0x1b4e4664de1d57b665b4bf3523cbccf007766de3\",\"value\":\"0xc8\",\"gasPrice\":\"0x3b9aca08\",\"gas\":\"0x1c9c37f\",\"data\":\"0xaeeb89600000000000000000000000000000000000000000000000000000000000000001\",\"input\":\"0xaeeb89600000000000000000000000000000000000000000000000000000000000000001\",\"type\":\"0x0\",\"v\":\"0x2c\",\"s\":\"0x20158ce3f4f9c65f8c657c0d91bbfb43632b2951f6192bca8fb3a25c26dd81d5\",\"r\":\"0x2814d998f2a78dd4f37461485d88158a32ef5dcfa8c57e224b3ea77536df01b1\"}}}")] + ResultWrapper txpool_content(); - [JsonRpcMethod(Description = "Returns tx pool content.", - IsImplemented = true, - ExampleResponse = "{\"0x0f990ef7ec160f01af7148b74cc8a86fe46c551e\":{\"153\":{\"hash\":\"0x84f6f2e5d24b9a0c25bd7018adbbf4388b2c07842782f73d5ddc389906d5f2c8\",\"nonce\":\"0x99\",\"blockHash\":null,\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x0f990ef7ec160f01af7148b74cc8a86fe46c551e\",\"to\":\"0x1b4e4664de1d57b665b4bf3523cbccf007766de3\",\"value\":\"0xc8\",\"gasPrice\":\"0x3b9aca08\",\"gas\":\"0x1c9c37f\",\"data\":\"0xaeeb89600000000000000000000000000000000000000000000000000000000000000001\",\"input\":\"0xaeeb89600000000000000000000000000000000000000000000000000000000000000001\",\"type\":\"0x0\",\"v\":\"0x2c\",\"s\":\"0x20158ce3f4f9c65f8c657c0d91bbfb43632b2951f6192bca8fb3a25c26dd81d5\",\"r\":\"0x2814d998f2a78dd4f37461485d88158a32ef5dcfa8c57e224b3ea77536df01b1\"}}}")] - ResultWrapper txpool_content(); + [JsonRpcMethod(Description = "Returns tx pool content for a specific sender address.", + IsImplemented = true, + ExampleResponse = "{\"pending\":{\"806\":{\"blockHash\":null,\"blockNumber\":null,\"blockTimestamp\":null,\"from\":\"0x0216d5032f356960cd3749c31ab34eeff21b3395\",\"gas\":\"0x5208\",\"gasPrice\":\"0xba43b7400\",\"hash\":\"0xaf953a2d01f55cfe080c0c94150a60105e8ac3d51153058a1f03dd239dd08586\",\"nonce\":\"0x326\",\"to\":\"0x7f69a91a3cf4be60020fb58b893b7cbb65376db8\",\"transactionIndex\":null,\"value\":\"0x19a99f0cf456000\"}},\"queued\":{}}")] + ResultWrapper txpool_contentFrom(Address address); - [JsonRpcMethod(Description = "Returns a detailed info on tx pool transactions.", - IsImplemented = true, - ExampleResponse = "{\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea\":{\"20\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6721975 × 140000000000 gas\",\"21\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6721975 × 140000000000 gas\",\"22\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6721975 × 140000000000 gas\",\"23\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6700000 × 140000000000 gas\",\"24\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6700000 × 140000000000 gas\",\"27\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6700000 × 140000000000 gas\"},\"0xc51db3339a7603f70b347a0b9680554f777d1f3c\":{\"82\":\"0xc51db3339a7603f70b347a0b9680554f777d1f3c: 0 wei + 4500000 × 10000000000 gas\"},\"0x084dd4aefc6853253573fee9f5fcc23e849d164c\":{\"17\":\"0x084dd4aefc6853253573fee9f5fcc23e849d164c: 0 wei + 28472169 × 1000000008 gas\"}}")] - ResultWrapper txpool_inspect(); - } + [JsonRpcMethod(Description = "Returns a detailed info on tx pool transactions.", + IsImplemented = true, + ExampleResponse = "{\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea\":{\"20\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6721975 × 140000000000 gas\",\"21\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6721975 × 140000000000 gas\",\"22\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6721975 × 140000000000 gas\",\"23\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6700000 × 140000000000 gas\",\"24\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6700000 × 140000000000 gas\",\"27\":\"0xb49928fcb10123e451cfe63aa47edcaea0f8aeea: 0 wei + 6700000 × 140000000000 gas\"},\"0xc51db3339a7603f70b347a0b9680554f777d1f3c\":{\"82\":\"0xc51db3339a7603f70b347a0b9680554f777d1f3c: 0 wei + 4500000 × 10000000000 gas\"},\"0x084dd4aefc6853253573fee9f5fcc23e849d164c\":{\"17\":\"0x084dd4aefc6853253573fee9f5fcc23e849d164c: 0 wei + 28472169 × 1000000008 gas\"}}")] + ResultWrapper txpool_inspect(); } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs index 192a526ad2d1..b2b45b4876fe 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; -using System.Linq; using Nethermind.Core; using Nethermind.Facade.Eth; using Nethermind.Facade.Eth.RpcTransaction; @@ -15,11 +14,27 @@ public class TxPoolContent public TxPoolContent(TxPoolInfo info, ulong chainId) { TransactionForRpcContext extraData = new(chainId); - Pending = info.Pending.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => TransactionForRpc.FromTransaction(v.Value, extraData))); - Queued = info.Queued.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => TransactionForRpc.FromTransaction(v.Value, extraData))); + Pending = MapByAddress(info.Pending, extraData); + Queued = MapByAddress(info.Queued, extraData); } - public Dictionary> Pending { get; set; } - public Dictionary> Queued { get; set; } + public Dictionary> Pending { get; set; } + public Dictionary> Queued { get; set; } + + private static Dictionary> MapByAddress( + Dictionary> source, + in TransactionForRpcContext extraData) + { + Dictionary> result = new(source.Count); + foreach (KeyValuePair> byAddress in source) + { + string key = ((Address)byAddress.Key).ToString(withZeroX: true, withEip55Checksum: true); + Dictionary txsByNonce = new(byAddress.Value.Count); + foreach (KeyValuePair kv in byAddress.Value) + txsByNonce[kv.Key] = TransactionForRpc.FromTransaction(kv.Value, extraData); + result[key] = txsByNonce; + } + return result; + } } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContentFrom.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContentFrom.cs new file mode 100644 index 000000000000..e188a997fb20 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContentFrom.cs @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.TxPool; + +namespace Nethermind.JsonRpc.Modules.TxPool; + +/// Response model for txpool_contentFrom: pending and queued transactions from a single address, keyed by nonce. +public class TxPoolContentFrom +{ + public TxPoolContentFrom(TxPoolInfo info, Address address, ulong chainId) + { + TransactionForRpcContext extraData = new(chainId); + AddressAsKey key = address; + Pending = MapTransactions(info.Pending, key, extraData); + Queued = MapTransactions(info.Queued, key, extraData); + } + + /// Transactions ready for inclusion in the next block, keyed by nonce. + public Dictionary Pending { get; } + + /// Transactions with nonce gaps awaiting preceding transactions, keyed by nonce. + public Dictionary Queued { get; } + + private static Dictionary MapTransactions( + Dictionary> source, + AddressAsKey key, + in TransactionForRpcContext extraData) + { + if (!source.TryGetValue(key, out IDictionary? txsByNonce)) + return new Dictionary(0); + + Dictionary result = new(txsByNonce.Count); + foreach (KeyValuePair kv in txsByNonce) + result[kv.Key] = TransactionForRpc.FromTransaction(kv.Value, extraData); + return result; + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolStatus.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolStatus.cs index d0272b0e077d..0d5042c966ea 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolStatus.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolStatus.cs @@ -4,11 +4,10 @@ using System.Linq; using Nethermind.TxPool; -namespace Nethermind.JsonRpc.Modules.TxPool +namespace Nethermind.JsonRpc.Modules.TxPool; + +public class TxPoolStatus(TxPoolInfo info) { - public class TxPoolStatus(TxPoolInfo info) - { - public int Pending { get; set; } = info.Pending.Sum(static t => t.Value.Count); - public int Queued { get; set; } = info.Queued.Sum(static t => t.Value.Count); - } + public ulong Pending { get; set; } = (ulong)info.Pending.Sum(static t => (long)t.Value.Count); + public ulong Queued { get; set; } = (ulong)info.Queued.Sum(static t => (long)t.Value.Count); } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs index b85847387fc8..13076904440b 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs @@ -1,33 +1,42 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.TxPool; -namespace Nethermind.JsonRpc.Modules.TxPool +namespace Nethermind.JsonRpc.Modules.TxPool; + +public class TxPoolRpcModule(ITxPoolInfoProvider txPoolInfoProvider, ISpecProvider specProvider) + : ITxPoolRpcModule { - public class TxPoolRpcModule(ITxPoolInfoProvider txPoolInfoProvider, ISpecProvider specProvider) - : ITxPoolRpcModule + public ResultWrapper txpool_status() { - public ResultWrapper txpool_status() - { - TxPoolInfo poolInfo = txPoolInfoProvider.GetInfo(); - TxPoolStatus poolStatus = new(poolInfo); + TxPoolInfo poolInfo = txPoolInfoProvider.GetInfo(); + TxPoolStatus poolStatus = new(poolInfo); + + return ResultWrapper.Success(poolStatus); + } - return ResultWrapper.Success(poolStatus); - } + public ResultWrapper txpool_content() + { + TxPoolInfo poolInfo = txPoolInfoProvider.GetInfo(); + ulong chainId = specProvider.ChainId; + return ResultWrapper.Success(new TxPoolContent(poolInfo, chainId)); + } - public ResultWrapper txpool_content() - { - TxPoolInfo poolInfo = txPoolInfoProvider.GetInfo(); - ulong chainId = specProvider.ChainId; - return ResultWrapper.Success(new TxPoolContent(poolInfo, chainId)); - } + public ResultWrapper txpool_contentFrom(Address address) + { + ArgumentNullException.ThrowIfNull(address); + TxPoolInfo poolInfo = txPoolInfoProvider.GetInfo(); + ulong chainId = specProvider.ChainId; + return ResultWrapper.Success(new TxPoolContentFrom(poolInfo, address, chainId)); + } - public ResultWrapper txpool_inspect() - { - TxPoolInfo poolInfo = txPoolInfoProvider.GetInfo(); - return ResultWrapper.Success(new TxPoolInspection(poolInfo)); - } + public ResultWrapper txpool_inspect() + { + TxPoolInfo poolInfo = txPoolInfoProvider.GetInfo(); + return ResultWrapper.Success(new TxPoolInspection(poolInfo)); } }