Skip to content
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;

namespace Nethermind.JsonRpc.Modules.TxPool
{
[RpcModule(ModuleType.TxPool)]
Expand All @@ -18,5 +20,8 @@ public interface ITxPoolRpcModule : IRpcModule
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<TxPoolInspection> txpool_inspect();

[JsonRpcMethod(Description = "Returns tx pool content from a specific address.", IsImplemented = true, ExampleResponse = "{\"pending\":{\"806\":{\"hash\":\"0xaf953a2d01f55cfe080c0c94150a60105e8ac3d51153058a1f03dd239dd08586\",\"nonce\":\"0x326\",\"blockHash\":null,\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x0216d5032f356960cd3749c31ab34eeff21b3395\",\"to\":\"0x7f69a91a3cf4be60020fb58b893b7cbb65376db8\",\"value\":\"0x19a99f0cf456000\",\"gasPrice\":\"0xba43b7400\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\"}},\"queued\":{}}")]
ResultWrapper<TxPoolContentFrom> txpool_contentFrom([JsonRpcParameter(ExampleValue = "0x0216d5032f356960cd3749c31ab34eeff21b3395")] Address address);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// 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;
using Nethermind.TxPool;

namespace Nethermind.JsonRpc.Modules.TxPool
{
public class TxPoolContentFrom
{
private static readonly IReadOnlyDictionary<ulong, TransactionForRpc> _emptyDictionary = new Dictionary<ulong, TransactionForRpc>();

public TxPoolContentFrom(TxPoolInfo info, ulong chainId, Address address)
{
TransactionForRpcContext extraData = new(chainId);
Pending = info.Pending.TryGetValue(address, out var pending)
? pending.ToDictionary(v => v.Key, v => TransactionForRpc.FromTransaction(v.Value, extraData))
: _emptyDictionary;
Queued = info.Queued.TryGetValue(address, out var queued)
? queued.ToDictionary(v => v.Key, v => TransactionForRpc.FromTransaction(v.Value, extraData))
: _emptyDictionary;
}

public IReadOnlyDictionary<ulong, TransactionForRpc> Pending { get; set; }
public IReadOnlyDictionary<ulong, TransactionForRpc> Queued { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;
using Nethermind.Core.Specs;
using Nethermind.TxPool;

Expand Down Expand Up @@ -29,5 +30,12 @@ public ResultWrapper<TxPoolInspection> txpool_inspect()
var poolInfo = txPoolInfoProvider.GetInfo();
return ResultWrapper<TxPoolInspection>.Success(new TxPoolInspection(poolInfo));
}

public ResultWrapper<TxPoolContentFrom> txpool_contentFrom(Address address)
{
TxPoolInfo poolInfo = txPoolInfoProvider.GetInfo(address);
ulong chainId = specProvider.ChainId;
return ResultWrapper<TxPoolContentFrom>.Success(new TxPoolContentFrom(poolInfo, chainId, address));
}
}
}
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.TxPool/ITxPoolInfoProvider.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;

namespace Nethermind.TxPool
{
public interface ITxPoolInfoProvider
{
TxPoolInfo GetInfo();
TxPoolInfo GetInfo(Address address);
}
}
41 changes: 41 additions & 0 deletions src/Nethermind/Nethermind.TxPool/TxPoolInfoProvider.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,58 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Linq;
using Nethermind.Core;
using Nethermind.Int256;

namespace Nethermind.TxPool
{
public class TxPoolInfoProvider(IAccountStateProvider accountStateProvider, ITxPool txPool) : ITxPoolInfoProvider
{
private static readonly Dictionary<AddressAsKey, IDictionary<ulong, Transaction>> _emptyAddressMap = [];

public TxPoolInfoProvider(IChainHeadInfoProvider chainHeadInfoProvider, ITxPool txPool) : this(chainHeadInfoProvider.ReadOnlyStateProvider, txPool) { }

public TxPoolInfo GetInfo(Address address)
{
Transaction[] transactions = txPool.GetPendingTransactionsBySender(address);

if (transactions.Length == 0)
return new TxPoolInfo([], []);

Array.Sort(transactions, static (a, b) => a.Nonce.CompareTo(b.Nonce));

UInt256 accountNonce = accountStateProvider.GetNonce(address);
UInt256 expectedNonce = accountNonce;
Dictionary<ulong, Transaction>? pending = null;
Dictionary<ulong, Transaction>? queued = null;

foreach (Transaction transaction in transactions)
{
ulong transactionNonce = (ulong)transaction.Nonce;
if (transaction.Nonce == expectedNonce)
{
(pending ??= new()).Add(transactionNonce, transaction);
expectedNonce = transaction.Nonce + 1;
}
else
{
(queued ??= new()).Add(transactionNonce, transaction);
}
}

Dictionary<AddressAsKey, IDictionary<ulong, Transaction>> pendingTransactions = pending is not null
? new(1) { [address] = pending }
: _emptyAddressMap;
Dictionary<AddressAsKey, IDictionary<ulong, Transaction>> queuedTransactions = queued is not null
? new(1) { [address] = queued }
: _emptyAddressMap;

return new TxPoolInfo(pendingTransactions, queuedTransactions);
}

public TxPoolInfo GetInfo()
{
// only std txs are picked here. Should we add blobs?
Expand Down