diff --git a/src/Nethermind/Nethermind.AccountAbstraction/Source/UserOperationPool.cs b/src/Nethermind/Nethermind.AccountAbstraction/Source/UserOperationPool.cs index 970dcb46f97..9e689610558 100644 --- a/src/Nethermind/Nethermind.AccountAbstraction/Source/UserOperationPool.cs +++ b/src/Nethermind/Nethermind.AccountAbstraction/Source/UserOperationPool.cs @@ -1,19 +1,19 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -// +// using System; using System.Collections.Concurrent; @@ -65,11 +65,11 @@ public class UserOperationPool : IUserOperationPool, IDisposable private readonly Channel _headBlocksReplacementChannel = Channel.CreateUnbounded(new UnboundedChannelOptions() { SingleReader = true, SingleWriter = true }); private readonly Channel _headBlockChannel = Channel.CreateUnbounded(new UnboundedChannelOptions() { SingleReader = true, SingleWriter = true }); - + private readonly ulong _chainId; private UInt256 _currentBaseFee; - + public UserOperationPool( IAccountAbstractionConfig accountAbstractionConfig, IBlockTree blockTree, @@ -106,7 +106,7 @@ ulong chainId _userOperationEventTopic = new Keccak("0x33fd4d1f25a5461bea901784a6571de6debc16cd0831932c22c6969cd73ba994"); MemoryAllowance.MemPoolSize = accountAbstractionConfig.UserOperationPoolSize; - + _blockTree.BlockAddedToMain += OnBlockAdded; _blockTree.NewHeadBlock += OnNewHead; @@ -129,7 +129,7 @@ private void OnBlockAdded(object? sender, BlockReplacementEventArgs e) $"Couldn't correctly add or remove user operations from UserOperationPool after processing block {e.Block!.ToString(Block.Format.FullHashAndNumber)}.", exception); } } - + private void OnNewHead(object? sender, BlockEventArgs e) { try @@ -170,7 +170,7 @@ private void ProcessNewReplacementBlocks() } }); } - + private void ProcessNewHeadBlocks() { Task.Factory.StartNew(async () => @@ -242,14 +242,14 @@ public bool CanInsert(UserOperation userOperation) } // we only want to increment opsSeen for ops whose maxFeePerGas passes baseFee - // else paymasters could be griefed by submitting many ops which might never + // else paymasters could be griefed by submitting many ops which might never // make it on chain but will increase opsSeen private void IncrementOpsSeenForOpsSurpassingBaseFee() { _userOperationSortedPool.UpdatePool(RemoveOpsSurpassingBaseFee); } - private IEnumerable<(UserOperation Tx, Action? Change)> RemoveOpsSurpassingBaseFee(Address address, ICollection userOperations) + private IEnumerable<(UserOperation Tx, Action? Change)> RemoveOpsSurpassingBaseFee(Address address, IReadOnlyCollection userOperations) { foreach (UserOperation op in userOperations) { @@ -280,7 +280,7 @@ public ResultWrapper AddUserOperation(UserOperation userOperation) UserOperationEventArgs userOperationEventArgs = new(userOperation, _entryPointAddress); NewReceived?.Invoke(this, userOperationEventArgs); - + UpdateCurrentBaseFee(); ResultWrapper result = ValidateUserOperation(userOperation); if (result.Result == Result.Success) @@ -295,9 +295,9 @@ public ResultWrapper AddUserOperation(UserOperation userOperation) _paymasterThrottler.IncrementOpsSeen(userOperation.Paymaster); } if (_logger.IsDebug) _logger.Debug($"UserOperation {userOperation.RequestId!} inserted into pool"); - _userOperationBroadcaster.BroadcastOnce(new UserOperationWithEntryPoint(userOperation, _entryPointAddress)); + _userOperationBroadcaster.BroadcastOnce(new UserOperationWithEntryPoint(userOperation, _entryPointAddress)); NewPending?.Invoke(this, userOperationEventArgs); - + return ResultWrapper.Success(userOperation.RequestId!); } @@ -371,7 +371,7 @@ private ResultWrapper ValidateUserOperation(UserOperation userOperation) { return ResultWrapper.Fail($"maxFeePerGas must be at least 70% of baseFee to be accepted into pool"); } - + PaymasterStatus paymasterStatus = _paymasterThrottler.GetPaymasterStatus(userOperation.Paymaster); diff --git a/src/Nethermind/Nethermind.Core/Collections/DictionarySortedSet.cs b/src/Nethermind/Nethermind.Core/Collections/DictionarySortedSet.cs index 2640c009363..ad61ee261b5 100644 --- a/src/Nethermind/Nethermind.Core/Collections/DictionarySortedSet.cs +++ b/src/Nethermind/Nethermind.Core/Collections/DictionarySortedSet.cs @@ -1,26 +1,26 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -// +// using System.Collections.Generic; using System.Runtime.Serialization; namespace Nethermind.Core.Collections { - public class DictionarySortedSet : SortedSet> + public class DictionarySortedSet : EnhancedSortedSet> { public DictionarySortedSet() : base(GetComparer()) { } @@ -32,12 +32,12 @@ public DictionarySortedSet(IEnumerable> collection) : public DictionarySortedSet(IEnumerable> collection, IComparer comparer) : base(collection, GetComparer(comparer)) { } protected DictionarySortedSet(SerializationInfo info, StreamingContext context) : base(info, context) { } - - private static IComparer> GetComparer(IComparer? comparer = null) => + + private static IComparer> GetComparer(IComparer? comparer = null) => new KeyValuePairKeyOnlyComparer(comparer ?? Comparer.Default); - + public bool Add(TKey key, TValue value) => Add(new KeyValuePair(key, value)); - + #pragma warning disable 8604 // fixed C# 9 public bool Remove(TKey key) => Remove(new KeyValuePair(key, default)); @@ -60,7 +60,7 @@ public bool TryGetValue(TKey key, out TValue value) #pragma warning restore 8601 return false; } - + #pragma warning disable 8604 // fixed C# 9 public bool ContainsKey(TKey key) => Contains(new KeyValuePair(key, default)); @@ -75,9 +75,9 @@ public KeyValuePairKeyOnlyComparer(IComparer? keyComparer = null) _keyComparer = keyComparer ?? Comparer.Default; } - public override int Compare(KeyValuePair x, KeyValuePair y) + public override int Compare(KeyValuePair x, KeyValuePair y) => _keyComparer.Compare(x.Key, y.Key); } - + } } diff --git a/src/Nethermind/Nethermind.Core/Collections/IReadOnlySortedSet.cs b/src/Nethermind/Nethermind.Core/Collections/IReadOnlySortedSet.cs new file mode 100644 index 00000000000..d0bdeb0d264 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Collections/IReadOnlySortedSet.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using System.Collections.Generic; + +namespace Nethermind.Core.Collections; + +public interface IReadOnlySortedSet : IReadOnlySet +{ + public T? Max { get; } + public T? Min { get; } +} diff --git a/src/Nethermind/Nethermind.Core/Collections/SortedSet.cs b/src/Nethermind/Nethermind.Core/Collections/SortedSet.cs new file mode 100644 index 00000000000..39b635f89b6 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Collections/SortedSet.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Nethermind.Core.Collections; + +public class EnhancedSortedSet : SortedSet, IReadOnlySortedSet +{ + public EnhancedSortedSet() { } + public EnhancedSortedSet(IComparer? comparer) : base(comparer) { } + public EnhancedSortedSet(IEnumerable collection) : base(collection) { } + public EnhancedSortedSet(IEnumerable collection, IComparer? comparer) : base(collection, comparer) { } + protected EnhancedSortedSet(SerializationInfo info, StreamingContext context) : base(info, context) { } +} diff --git a/src/Nethermind/Nethermind.Facade.Test/Proxy/EthJsonRpcClientProxyTests.cs b/src/Nethermind/Nethermind.Facade.Test/Proxy/EthJsonRpcClientProxyTests.cs index a3900d56db7..159e1cfd2ff 100644 --- a/src/Nethermind/Nethermind.Facade.Test/Proxy/EthJsonRpcClientProxyTests.cs +++ b/src/Nethermind/Nethermind.Facade.Test/Proxy/EthJsonRpcClientProxyTests.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -75,7 +75,7 @@ public async Task eth_getTransactionCount_should_invoke_client_method() var address = TestItem.AddressA; var blockParameter = BlockParameterModel.Latest; await _proxy.eth_getTransactionCount(address, blockParameter); - await _client.Received().SendAsync(nameof(_proxy.eth_getTransactionCount), + await _client.Received().SendAsync(nameof(_proxy.eth_getTransactionCount), address, blockParameter.Type); } @@ -114,7 +114,7 @@ public async Task eth_getTransactionByHash_should_invoke_client_method() await _proxy.eth_getTransactionByHash(hash); await _client.Received().SendAsync(nameof(_proxy.eth_getTransactionByHash), hash); } - + [Test] public async Task eth_pendingTransactions_should_invoke_client_method() { diff --git a/src/Nethermind/Nethermind.Facade/Proxy/EthJsonRpcClientProxy.cs b/src/Nethermind/Nethermind.Facade/Proxy/EthJsonRpcClientProxy.cs index cc21cc0c2b1..e3bd56cb2b3 100644 --- a/src/Nethermind/Nethermind.Facade/Proxy/EthJsonRpcClientProxy.cs +++ b/src/Nethermind/Nethermind.Facade/Proxy/EthJsonRpcClientProxy.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -41,12 +41,12 @@ public Task> eth_chainId() public Task> eth_getBalance(Address address, BlockParameterModel blockParameter = null) => _proxy.SendAsync(nameof(eth_getBalance), address, MapBlockParameter(blockParameter)); - public Task> eth_getTransactionCount(Address address, BlockParameterModel blockParameter = null) - => _proxy.SendAsync(nameof(eth_getTransactionCount), address, MapBlockParameter(blockParameter)); + public Task> eth_getTransactionCount(Address address, BlockParameterModel blockParameter = null) + => _proxy.SendAsync(nameof(eth_getTransactionCount), address, MapBlockParameter(blockParameter)); public Task> eth_getTransactionReceipt(Keccak transactionHash) => _proxy.SendAsync(nameof(eth_getTransactionReceipt), transactionHash); - + public Task> eth_call(CallTransactionModel transaction, BlockParameterModel blockParameter = null) => _proxy.SendAsync(nameof(eth_call), transaction, MapBlockParameter(blockParameter)); @@ -56,7 +56,7 @@ public Task> eth_getCode(Address address, BlockParameterModel public Task> eth_getTransactionByHash(Keccak transactionHash) => _proxy.SendAsync(nameof(eth_getTransactionByHash), transactionHash); - + public Task> eth_pendingTransactions() => _proxy.SendAsync(nameof(eth_pendingTransactions)); @@ -65,7 +65,7 @@ public Task> eth_sendRawTransaction(byte[] transaction) public Task> eth_sendTransaction(TransactionModel transaction) => _proxy.SendAsync(nameof(eth_sendTransaction), transaction); - + public Task> eth_estimateGas(TransactionModel transaction, BlockParameterModel blockParameter = null) => _proxy.SendAsync(nameof(eth_estimateGas), transaction); @@ -77,12 +77,12 @@ public Task>> eth_getBlockByNumber(BlockParameterMo bool returnFullTransactionObjects = false) => _proxy.SendAsync>(nameof(eth_getBlockByNumber), MapBlockParameter(blockParameter), returnFullTransactionObjects); - + public Task>> eth_getBlockByNumberWithTransactionDetails(BlockParameterModel blockParameter, bool returnFullTransactionObjects = false) => _proxy.SendAsync>(nameof(eth_getBlockByNumber), MapBlockParameter(blockParameter), returnFullTransactionObjects); - + public Task> net_version() => _proxy.SendAsync(nameof(net_version)); diff --git a/src/Nethermind/Nethermind.Facade/Proxy/IEthJsonRpcClientProxy.cs b/src/Nethermind/Nethermind.Facade/Proxy/IEthJsonRpcClientProxy.cs index b75784a3675..1e3a293b21e 100644 --- a/src/Nethermind/Nethermind.Facade/Proxy/IEthJsonRpcClientProxy.cs +++ b/src/Nethermind/Nethermind.Facade/Proxy/IEthJsonRpcClientProxy.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -27,7 +27,7 @@ public interface IEthJsonRpcClientProxy Task> eth_chainId(); Task> eth_blockNumber(); Task> eth_getBalance(Address address, BlockParameterModel blockParameter = null); - Task> eth_getTransactionCount(Address address, BlockParameterModel blockParameter = null); + Task> eth_getTransactionCount(Address address, BlockParameterModel blockParameter = null); Task> eth_getTransactionReceipt(Keccak transactionHash); Task> eth_call(CallTransactionModel transaction, BlockParameterModel blockParameter = null); Task> eth_getCode(Address address, BlockParameterModel blockParameter = null); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs index f480875eabf..409f5f6893a 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -71,7 +71,7 @@ public async Task Eth_get_balance_default_block() string serialized = ctx.Test.TestEthRpc("eth_getBalance", TestItem.AddressA.Bytes.ToHexString(true)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x3635c9adc5de9f09e5\",\"id\":67}", serialized); } - + [Test] public async Task Eth_get_eth_feeHistory() { @@ -95,7 +95,7 @@ public async Task Eth_get_transaction_by_hash() string serialized = ctx.Test.TestEthRpc("eth_getTransactionByHash", ctx.Test.BlockTree.FindHeadBlock()!.Transactions.Last().Hash!.ToString()); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":{\"hash\":\"0x7126cf20a0ad8bd51634837d9049615c34c1bff5e1a54e5663f7e23109bff48b\",\"nonce\":\"0x2\",\"blockHash\":\"0x29f141925d2d8e357ae5b6040c97aa12d7ac6dfcbe2b20e7b616d8907ac8e1f3\",\"blockNumber\":\"0x3\",\"transactionIndex\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"data\":\"0x\",\"input\":\"0x\",\"type\":\"0x0\",\"v\":\"0x25\",\"s\":\"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb\",\"r\":\"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd\"},\"id\":67}", serialized, serialized.Replace("\"", "\\\"")); } - + [Test] public async Task eth_maxPriorityFeePerGas_test() { @@ -112,7 +112,7 @@ public async Task Eth_pending_transactions() string serialized = ctx.Test.TestEthRpc("eth_pendingTransactions"); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[{\"hash\":\"0x190d9a78dbc61b1856162ab909976a1b28ba4a41ee041341576ea69686cd3b29\",\"nonce\":\"0x0\",\"blockHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"data\":\"0x\",\"input\":\"0x\",\"type\":\"0x0\",\"v\":\"0x26\",\"s\":\"0x2d04e55699fa32e6b65a22189f7571f5030d636d7d44a8b53fe016a2c3ecde24\",\"r\":\"0xda3978c3a1430bd902cf5bbca73c5a1eca019b3f003c95ee16657fd0bb89534c\"}],\"id\":67}", serialized, serialized.Replace("\"", "\\\"")); } - + [Test] public async Task Eth_pending_transactions_1559_tx() { @@ -122,7 +122,7 @@ public async Task Eth_pending_transactions_1559_tx() string serialized = ctx.Test.TestEthRpc("eth_pendingTransactions"); serialized.Contains(addedTx).Should().BeTrue(); } - + [Test] public async Task Eth_pending_transactions_2930_tx() { @@ -172,7 +172,7 @@ public async Task Eth_get_uncle_by_block_hash_and_index(bool eip1559, string exp ReleaseSpec releaseSpec = new() {IsEip1559Enabled = true, Eip1559TransitionBlock = 1 }; specProvider.GetSpec(Arg.Any()).Returns(releaseSpec); } - + using Context ctx = await Context.Create(); Block block = Build.A.Block.WithUncles(Build.A.BlockHeader.WithNumber(2).TestObject, Build.A.BlockHeader.TestObject).WithNumber(3).TestObject; IBlockTree blockTree = Substitute.For(); @@ -189,7 +189,7 @@ public async Task Eth_get_uncle_count_by_block_hash() string serialized = ctx.Test.TestEthRpc("eth_getUncleCountByBlockHash", ctx.Test.BlockTree.FindHeadBlock()!.Hash!.ToString()); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x0\",\"id\":67}", serialized, serialized.Replace("\"", "\\\"")); } - + [Test] public async Task Eth_get_uncle_count_by_block_number() { @@ -200,11 +200,24 @@ public async Task Eth_get_uncle_count_by_block_number() [TestCase("earliest", "0x0")] [TestCase("latest", "0x3")] - [TestCase("pending", "0x3")] + [TestCase("pending", "0x4")] [TestCase("0x0", "0x0")] public async Task Eth_get_tx_count(string blockParameter, string expectedResult) { using Context ctx = await Context.Create(); + + // Add two transactions, one with the next nonce (nonce=3) and the second one with a gap in nonce (nonce=5, skipping nonce=4) + Transaction txWithNextNonce = Build.A.Transaction.To(TestItem.AddressB) + .SignedAndResolved(TestItem.PrivateKeyA).WithValue(0.Ether()).WithNonce(3).TestObject; + Transaction txWithFutureNonce = Build.A.Transaction.To(TestItem.AddressB) + .SignedAndResolved(TestItem.PrivateKeyA).WithValue(0.Ether()).WithNonce(5).TestObject; + ValueTask<(Keccak? Hash, AcceptTxResult? AddTxResult)> resultNextNonce = + ctx.Test.TxSender.SendTransaction(txWithNextNonce, TxHandlingOptions.None); + ValueTask<(Keccak? Hash, AcceptTxResult? AddTxResult)> resultFutureNonce = + ctx.Test.TxSender.SendTransaction(txWithFutureNonce, TxHandlingOptions.None); + Assert.AreEqual(resultNextNonce.Result.AddTxResult, AcceptTxResult.Accepted); + Assert.AreEqual(resultFutureNonce.Result.AddTxResult, AcceptTxResult.Accepted); + string serialized = ctx.Test.TestEthRpc("eth_getTransactionCount", TestItem.AddressA.Bytes.ToHexString(true), blockParameter); Assert.AreEqual($"{{\"jsonrpc\":\"2.0\",\"result\":\"{expectedResult}\",\"id\":67}}", serialized); } @@ -216,7 +229,24 @@ public async Task Eth_get_tx_count_default_block() string serialized = ctx.Test.TestEthRpc("eth_getTransactionCount", TestItem.AddressA.Bytes.ToHexString(true)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x3\",\"id\":67}", serialized); } - + + [Test] + public async Task Eth_get_tx_count_pending_block() + { + using Context ctx = await Context.Create(); + string serializedPendingBefore = ctx.Test.TestEthRpc("eth_getTransactionCount", TestItem.AddressB.Bytes.ToHexString(true), "pending"); + Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x0\",\"id\":67}", serializedPendingBefore); + Transaction txWithNextNonce = Build.A.Transaction.To(TestItem.AddressA) + .SignedAndResolved(TestItem.PrivateKeyB).WithValue(0.Ether()).WithNonce(0).TestObject; + ValueTask<(Keccak? Hash, AcceptTxResult? AddTxResult)> resultNextNonce = + ctx.Test.TxSender.SendTransaction(txWithNextNonce, TxHandlingOptions.None); + Assert.AreEqual(resultNextNonce.Result.AddTxResult, AcceptTxResult.Accepted); + string serializedLatestAfter = ctx.Test.TestEthRpc("eth_getTransactionCount", TestItem.AddressB.Bytes.ToHexString(true)); + Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x0\",\"id\":67}", serializedLatestAfter); + string serializedPendingAfter = ctx.Test.TestEthRpc("eth_getTransactionCount", TestItem.AddressB.Bytes.ToHexString(true), "pending"); + Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x1\",\"id\":67}", serializedPendingAfter); + } + [Test] public async Task Eth_get_filter_changes_empty() { @@ -225,7 +255,7 @@ public async Task Eth_get_filter_changes_empty() string serialized2 = ctx.Test.TestEthRpc("eth_getFilterChanges", "0"); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[],\"id\":67}", serialized2); } - + [Test] public async Task Eth_uninstall_filter() { @@ -234,7 +264,7 @@ public async Task Eth_uninstall_filter() string serialized2 = ctx.Test.TestEthRpc("eth_uninstallFilter", "0"); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":67}", serialized2); } - + [Test] public async Task Eth_get_filter_changes_with_block() { @@ -245,7 +275,7 @@ public async Task Eth_get_filter_changes_with_block() Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[\"0x166781de9c5f3328b7fc59c32e1dd1ec892021d95578258004ee221863a817a0\"],\"id\":67}", serialized2, serialized2.Replace("\"", "\\\"")); } - + [Test] public async Task Eth_get_filter_changes_with_tx() { @@ -366,7 +396,7 @@ public async Task Eth_get_logs(string parameter, string expected) Assert.AreEqual(expected, serialized); } - + [TestCase("{\"fromBlock\":\"earliest\",\"toBlock\":\"latest\"}", "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32001,\"message\":\"resource not found message\"},\"id\":67}")] public async Task Eth_get_logs_with_resourceNotFound(string parameter, string expected) { @@ -542,7 +572,7 @@ public async Task Eth_accounts() string serialized = ctx.Test.TestEthRpc("eth_accounts"); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[\"0x7e5f4552091a69125d5dfcb7b8c2659029395bdf\",\"0x2b5ad5c4795c026514f8317c7a215e218dccd6cf\",\"0x6813eb9362372eef6200f3b1dbc3f819671cba69\",\"0x1eff47bc3a10a45d4b230b5d10e37751fe6aa718\",\"0xe1ab8145f7e55dc933d51a18c793f901a3a0b276\",\"0xe57bfe9f44b819898f47bf37e5af72a0783e1141\",\"0xd41c057fd1c78805aac12b0a94a405c0461a6fbb\",\"0xf1f6619b38a98d6de0800f1defc0a6399eb6d30c\",\"0xf7edc8fa1ecc32967f827c9043fcae6ba73afa5c\",\"0x4cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528\",\"0x3da8d322cb2435da26e9c9fee670f9fb7fe74e49\",\"0xdbc23ae43a150ff8884b02cea117b22d1c3b9796\",\"0x68e527780872cda0216ba0d8fbd58b67a5d5e351\",\"0x5a83529ff76ac5723a87008c4d9b436ad4ca7d28\",\"0x8735015837bd10e05d9cf5ea43a2486bf4be156f\",\"0xfae394561e33e242c551d15d4625309ea4c0b97f\",\"0x252dae0a4b9d9b80f504f6418acd2d364c0c59cd\",\"0x79196b90d1e952c5a43d4847caa08d50b967c34a\",\"0x4bd1280852cadb002734647305afc1db7ddd6acb\",\"0x811da72aca31e56f770fc33df0e45fd08720e157\",\"0x157bfbecd023fd6384dad2bded5dad7e27bf92e4\",\"0x37da28c050e3c0a1c0ac3be97913ec038783da4c\",\"0x3bc8287f1d872df4217283b7920d363f13cf39d8\",\"0xf4e2b0fcbd0dc4b326d8a52b718a7bb43bdbd072\",\"0x9a5279029e9a2d6e787c5a09cb068ab3d45e209d\",\"0xc39677f5f47d5fe65ab24e66750e8fca127c15be\",\"0x1dc728786e09f862e39be1f39dd218ee37feb68d\",\"0x636cc65783084b9f370789c90f733dbbeb88925d\",\"0x4a7a7c2e09209dbe44a582cd92b0edd7129e74be\",\"0xa56160a359f2eaa66f5c9df5245542b07339a9a6\",\"0x6b09d6433a379752157fd1a9e537c5cae5fa3168\",\"0x32e77de0d74a5c7af861aaed324c6a4c488142a8\",\"0x093d49d617a10f26915553255ec3fee532d2c12f\",\"0x138854708d8b603c9b7d4d6e55b6d32d40557f4d\",\"0x7dc0a40d64d72bb4590652b8f5c687bf7f26400c\",\"0x9358a525cc25aa571af0bcb5b98fbeab045a5e36\",\"0xd8e8ea89d71de89214fa39ba13ba9fcddc0d9467\",\"0xb56ed8f48979e1a948ad129199a600d0562cac51\",\"0xf65ac7003e905d72c666bfec1dc0960ecc9d0d6e\",\"0xd817d23c981472d703be36da777ffdb1abefd972\",\"0xf2adb90aa27a3c61a95c50063b20919d811e1476\",\"0xae3dffee97f92db0201d11cb8877c89738353bce\",\"0xeb3025e7ac2764040384316b33476e048961a71f\",\"0x9e3289708dc5709926a542fcf260fd4b210461f0\",\"0x6c23face014f20b3ebb65ae96d0d7ff32ab94c17\",\"0xb83b6241f966b1685c8b2ffce3956e21f35b4dcb\",\"0x6350872d7465864689def650443026f2f73a08da\",\"0x673c638147fe91e4277646d86d5ae82f775eea5c\",\"0xf472086186382fca55cd182de196520abd76f69d\",\"0x5ae58d2bc5145bff0c1bec0f32bfc2d079bc66ed\",\"0x2b29bea668b044b2b355c370f85b729bcb43ec40\",\"0x3797126345fb5fb6a37629db55ec692173cfb458\",\"0xe6869cc98283ab53e8a1a5312857ef0be9d189fe\",\"0xa5dfe354b3fc30c5c3a8ffefc8f9470d9177c334\",\"0xa1a625ae13b80a9c48b7c0331c83bc4541ac137f\",\"0xa33c9d26e1e33b84247defca631c1d30ffc76f5d\",\"0xf9807a8719ae154e74591cb2d452797707fadf73\",\"0xa1ba6fc3ea0e89f0e79f89d9aa0081d010571e4a\",\"0x366c20b40048556e5682e360997537c3715aca0e\",\"0xeb0e56f32246d043228fac8b63a71687d5199af1\",\"0xdb3ed822b78f0641623a12166607b5fa4df862ad\",\"0xb88c19426f03c6981d1a4281c7414d842b97619a\",\"0x32e04b012ac811c91d36a355a6d2859a0071a965\",\"0xe0dd44773f7657b11019062879d65f3d9862460c\",\"0x756be12856a8f44ab22fdbcbd42b70b843377d09\",\"0x6f4c950442e1af093bcff730381e63ae9171b87a\",\"0x4d1bf28514a4451249908e611366ec967c3d1558\",\"0xb0142d883494197b02c6ece84f571d81bd831124\",\"0x1326324f5a9fb193409e10006e4ea41b970df321\",\"0xf9a2c330a19e2fbfeb50fe7a7195b973bb0a3be9\",\"0x7a601ffa997cede6435aeabf4fa2091f09e149ec\",\"0xa92f4b5c4fddcc37e5139873ac28a4a0a42d68df\",\"0x850cc185d6cae4a7fdfb3dd81f977dd1df7d6503\",\"0xb1b7c87e8a0bf2e7fd1a1c582bd353e4c4529341\",\"0xff844fdb49e00776ad538db9ea2f9fa98ec0caf7\",\"0x1ac6f9601f2f616badcea8a0a307e1a3c14767a4\",\"0xc2aa6271409c10dee630e79df90c968989ccf2b7\",\"0x883d01eae6eaac077e126ddb32cd53550966ed76\",\"0x127688bbc070dd69a4db8c3ba5d43909e13d8f77\",\"0x0b54a50c0409dab2e63c3566324268ed53ec019a\",\"0xafd46e3549cc63d7a240d6177d056857679e6f99\",\"0x752481f35bb1d44d786c7b4dbe40db4a4266f96f\",\"0xac32def421e36b43629f785fd04523260e7f2b28\",\"0xfe6032a0810e90d025a3a39dd29844f964ee102c\",\"0x5cb6f3e6499d1f068b33351d0cae4b68cdf501bf\",\"0x84b743441b7bdf65cb4293126db4c1b709d7d95e\",\"0x8530a26f6c062f55597bd30c1a44e248decb0027\",\"0x5ce162cfa6208d7c50a7cb3525ac126155e7bce4\",\"0x2853dc9ca40d012969e25360cce0d9d326b24a86\",\"0x802271c02f76701929e1ea772e72783d28e4b60f\",\"0x7bd2aa0726ac3b9e752b120de8568e90b0423ae4\",\"0xb540c05d9b2516da9596a5ee75d750717a4be035\",\"0xa72392cd4be285ab6681da1bf1c45f0b370cb7b4\",\"0xcf484269182ac2388a4bfe6d19fb0687e3534b7f\",\"0x994907cb80bfd175f9b0b32672cfde0091368e2e\",\"0x36eab6ce7fededc098ef98c41e83548a89147131\",\"0x440db3ab842910d2a39f4e1be9e017c6823fb658\",\"0x25ac70ea6f44c4531a7117ea3620fa29cdaaca48\",\"0x24d881139ee639c2a774b4b1851cb7a9d0fce122\",\"0xd9a284367b6d3e25a91c91b5a430af2593886eb9\",\"0xe6b3367318c5e11a6eed3cd0d850ec06a02e9b90\",\"0x88c0e901bd1fd1a77bda342f0d2210fdc71cef6b\",\"0x7231c364597f3bfdb72cf52b197cc59111e71794\",\"0x043aed06383f290ee28fa02794ec7215ca099683\",\"0x0c95931d95694b3ef74071241827c09f25d40620\",\"0x417f3b59ef57c641283c2300fae0f27fe98d518c\",\"0xd6b931d8d441b1ec98f55f8ec8adb532dc140c78\",\"0x9220625b1a30680387d542e6b5f753786ca5530e\",\"0x997cf669860a1dcc76344866534d8679a7b562e2\",\"0xb961768b578514debf079017ff78c47b0a6adbf6\",\"0x052b91ad9732d1bce0ddae15a4545e5c65d02443\",\"0x8df64de79608f0ae9e72ecae3a400582aed8101c\",\"0x0e7b23cd1fdb7ea3ccc80320ab43843a2f193c36\",\"0xfbbc41289f834a76e4320ac238512035560467ee\",\"0x61e1da6c7b8b211e6e5dd921efe27e73ad226dac\",\"0x87fcbe64187317c59a944be5b9c5c830b9373730\",\"0x2acf0d6fdac920081a446868b2f09a8dac797448\",\"0x1715eb68afba4d516ef1e068b55f5093bb4a2f59\",\"0x58bab2f728dc4fc227a4c38cab2ec93b73b4e828\",\"0x25346934b4faa00dee0190c2069156bde6010c18\",\"0xa01cca6367a84304b6607b76676c66c360b74741\",\"0x872917cec8992487651ee633dba73bd3a9dca309\",\"0x6c1a01c2ab554930a937b0a2e8105fb47946c679\",\"0x13c0e7c715fdea35c7f9663c719e4d36601275b9\",\"0xe8c5025c803f3279d94ea55598e147f601929bf4\",\"0x639acdbd838b81cea8d6a970136812783fa5bf5e\",\"0xb3087f34edab33a8182ba29adea4d739d9831a94\",\"0xc6a210606f2ee6e64afb9584db054f3476a5cc66\",\"0xd01c9d93efc83c00b30f768f832182beff65696f\",\"0x00edf2d16afbc028fb1e879559b07997af79539f\",\"0xf5d722030d17ca01f2813af9e7be158d7a037997\",\"0xae3d43ab6fdcd35386db427099ff11aa670ee0f4\",\"0x0dc8b8ef8457b1e45ac277d65ac5987b547ba775\",\"0xde521346f9327a4314a18b2cda96b2a33603177a\",\"0x69842e12d6f36f9f93f06086b70795bfc7e02745\",\"0x9b7bdf6ad17d5fc9a168acaa24495e52a65f3b79\",\"0xa2d47d2c42009520075cb15f5855052008d0c44d\",\"0xb0c249f6f92fb2491fc9750a5299d856ba2ea3c6\",\"0x839d96957f21e82fbcca0d42a1f12ef6e1cf82e9\",\"0x2a0d6b92b042497013e5549d6579202608ce0c80\",\"0xa4f8c598927eab2f1898f8f2d6f8121578de2344\",\"0xdb21655b672dacc8da6f538c899f9d6969604117\",\"0x21289cd01f9f58fc44962b6e213a0fbbd015beb6\",\"0x0b62d63c314d94dfa85b11a9c652ffe438382d6c\",\"0x9383e3096133f464d516b518b12851fd10d891f4\",\"0x64e582c17ab7c3b90e171795b504ca3c04108501\",\"0x848406919d014b1e5c27a82f951caff840fd63ef\",\"0x5fe015779fb36006b01f9c5a5dbcaa6ffa56f0c0\",\"0x28b6e15f86025b8ea8beaa6855a81069bfb6ab1e\",\"0x271d65af9a5a7b4cd7af264f251184c2a4b9e7a3\",\"0xddf44e34ed40c40624c7b9f20a1030b505a4fac0\",\"0xe5854075272ca5ef71663d5b87e0cd5ac53b2f36\",\"0x2798ba84d7830c5f60d750f37f87d93277106905\",\"0x7e9961fa09dd52f945f8143844785cf0e51bb4ce\",\"0xf33d2f7d96f92d912ca8418f9d62eb54c1a9889f\",\"0xeec566c793a89f388bbabfc0225183a6a95c4263\",\"0x2001f8cdcdeef1bbcc188ca59cf04fb44133d55a\",\"0x3bf958fa0626e898f548a8f95cf9ab3a4db65169\",\"0xb0d744fde06bbcb6655eb55288ec94fa6a0b2a52\",\"0x18eb36d090eeadf82f3454a6da690fc398d3eba1\",\"0xd2431ca38735c2fd438e2caa23f094191d89675b\",\"0x612b7be154a64292aae070aaa86fcd66ba218071\",\"0x681ce2f439fdc80e70c1eea8b8a085dfb976d32a\",\"0x2174ca3ee9ace7dd8c946c97054c72f2b384c4c2\",\"0x1d694d5ad94f32132ff5c14c901d3ddbee90a550\",\"0x0b6fe046e6fd8d7a7a36d5ad1ffb82d2e3e5c3bb\",\"0x258f4ed0560e290a95066d9dee3628f2f179302b\",\"0xe2a09565167d4e3f826adec6bef82b97e0a4383f\",\"0x9af70704e9ec5f505cdba564ff4dec03503ddaa2\",\"0xeb9afe072c781401bf364224c75a036e4d832f52\",\"0x07748403082b29a45abd6c124a37e6b14e6b1803\",\"0x63486b70d804464766cfd096bba5552c4bcdac30\",\"0x5181be40152caaba8e123a55b7762755d4e8e416\",\"0x9481da7766c043eefeecc9589ee7ade61316b0ff\",\"0x42aba3530dd1ccb1dda27bfaa7c6a832cfdb4446\",\"0x05650444ace15a01762bd97ee8fdeb495b3c2436\",\"0xd83d18a2eae2440e272a53f86e617cd9f33c8d68\",\"0x4a35a802dbd623561040dd50f6293842d0901731\",\"0x4dbaf6c348d8cd1f174a7a6155f80ea8d4a8baf8\",\"0x9efc4e49be8ff70d596ac20efec9b7842e1ea963\",\"0x68efde0cdd917c6da6dab02c23f69e7c9cff51a9\",\"0x99b52813933a46d95bd4265ea2f674e58827da97\",\"0x7b35461cc5adbdc415c1f9562ccc342adbf09bd4\",\"0x8ee8813fb9d41cc58ef87d28b36e948b1234e71d\",\"0x69c1bc7883a7bb7696c7726d025867cd16564c9c\",\"0x31eb18dd6f5a8064ab750eabb281cf162f43ccd0\",\"0xf5d122e123d9d7998d2bea685d11b10fec3e4508\",\"0xf762854586a40a93d1fdcde32c062829f3754de9\",\"0x1e3f8fb9f840325983d6e5c68b6b846ff66a20ac\",\"0x3c1638a25ad7e8c2a84b53b661dd1bd048407e8f\",\"0x2eedf8a799b73bc02e4664183eb72422c377153b\",\"0xcdef6f23a26f960b53468f497967ce74c026af52\",\"0x0a2035683fe5587b0b09db0e757306ad729fe6c1\",\"0x158cc083cfbcffb2f983a3aa8b027eb0711c9831\",\"0x691cb1645a4f21d879973b3a3b98a714fc1970d6\",\"0x754164c0cb85dda1b5b18e5b62adbb4d60c3efbf\",\"0x556330e8d92912ccf133851ba03abd2db70da404\",\"0x1745ceba112b0a41638e235ec59b35adf37b70ab\",\"0xa24c85b16a440587793f82e358fa6b204468735b\",\"0x5304fb08724d73f2bb5e04c582407c33cde6c8d3\",\"0x256a11785fc43141324cf61efb5f491378c10c85\",\"0xa9f161a2badd44f3fe45b91a044a9484b72f1dc4\",\"0xd5cc10c45fc0f9f956acd7559f61edbfec9f6c3d\",\"0x381c7a71035bdb42fb5d77523df2ff00d9f9df1b\",\"0x45cdbeea730d8212f451a6a8d0eb5998b04cccca\",\"0x6367283f25a32be0c28623d787c319e237c3b7bd\",\"0x598e94eb5e050045272d8417f6ab363bd874d568\",\"0x379ff6375f4a44f458683629ef05141d73fae55a\",\"0x18df8ba2ef19083ddff68f8b33976cf22e8419c6\",\"0xffceebc37a7351d5df9aa3b077ed39cc3b5192ff\",\"0x1cd21f00b58894260f7abed65ad23dce3cea0226\",\"0x26324733d604abb6176cf18e4f4a0624ceeddc09\",\"0x4102d394d723ff141b82ef9a6053fb89f90c67f4\",\"0x269c370cb95b63f9b6a7cad47998167f160a2689\",\"0x3bb9557113fbb052dae3008a2801a072c432c018\",\"0x3f588a72d94d0d0986b112c671c2343320a19386\",\"0x7cfa9eee1d752da599211bc8a68d0687708dabfc\",\"0x7bc23966c419eadcb8a2fc5f83e635c4d4ad0c2f\",\"0xc4ad60337b04fc721912531a52a5d77878293fb9\",\"0xfc5ba3041f750f9b6820ce066c153eb396aac1ff\",\"0x32480c2d857941d2fff4e34f0910b20c0f9c23bc\",\"0x8041c9a96585053db2d7214b5de56828645b8e62\",\"0x444ca66b3ceb4187840cb1a205566a1413d5fecb\",\"0xf084bbaabee1a700a8faa404027db620a5aa0059\",\"0x602d562b4ef2544f851587619b56f77a9d965d45\",\"0x216faec139a61329ef8b31d982de427d9c007a9e\",\"0x11eb17b20113ae923d72e52870d40bf59a08b40d\",\"0xe69017fcc36bbc7fb167b9585bdd47a950ba1992\",\"0xe5549f429a72bfa618cf5c1afdac22a730df6a1a\",\"0x161c2e10407e2a87959c0bae1f342c80eaea28f3\",\"0x4161220db043a7d682e0ad123a3f8fea165711aa\",\"0xb33609811fb3d9fc8955dc6e9e086f1f08fc3a65\",\"0x4148555ea4c00e14f81ef399bbe67ef2fd9811b1\",\"0x4f81e991f76276a17ca92a1321f37189b1727f77\",\"0xba95e317ead06b55c8b70276fc63904b3339dfa1\",\"0xf6203c4fb14da640d11fbd9573e3958d017e6745\",\"0x73377d6228266393747efa710017872d6dd5b9a6\",\"0xf7862d105fc6ee69604decc30aa89472ad405961\",\"0xfa1205e19719c248323563bd55cd8bfd08b0cbc6\",\"0x4f46630115b446f8f7cebe1e5961ef7858c25204\",\"0x7492ebbc1e7f2838fc7191edc031581d5712978a\",\"0xc0af3981f9c0dfcb8955fea07a3e4f23806fab51\",\"0x8621dd642245df371b584b48c081e8863313a70d\",\"0xc328de035c91b39efa07d2fe620813253c9b4ec2\",\"0xa11308e3b741227d41973ddb17534ceb27b8206f\",\"0xc4ff1b4565ee203fa12636e100fe9c89cd18acb7\",\"0x63a36aea8570219476ef835f09024acdedfee95a\",\"0xf7205066c153f7c88dc3653ebc72b438884ae109\",\"0xa8ce5c40c4aa9278ddeaa418e775985549960e7a\",\"0x81f58f2194b0413806bf2ce8e1654bc855dc65a1\",\"0xf0218008120201e66b65fce4df9035007e811228\",\"0x90f022e3ca8453f5e5765cd3054003b544526eec\",\"0x1d1f873ba1ddf7915e6e26f93f5624b40efaefe2\",\"0x0311afd3bc2ae250d5f9f2706bae2ef4164d6912\",\"0x5044a80bd3eff58302e638018534bbda8896c48a\"],\"id\":67}", serialized, serialized.Replace("\"", "\\\"")); } - + [Test] public async Task Eth_get_block_by_number_with_number_bad_number() { @@ -574,21 +604,21 @@ public async Task Eth_get_transaction_receipt() IBlockchainBridge blockchainBridge = Substitute.For(); IBlockFinder blockFinder = Substitute.For(); IReceiptFinder receiptFinder = Substitute.For(); - + Block block = Build.A.Block.WithNumber(1) .WithStateRoot(new Keccak("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")) .TestObject; - + LogEntry[] entries = new[] { Build.A.LogEntry.TestObject, Build.A.LogEntry.TestObject }; - + TxReceipt receipt = Build.A.Receipt.WithBloom(new Bloom(entries, new Bloom())).WithAllFieldsFilled .WithLogs(entries).TestObject; TxReceipt[] receiptsTab = {receipt}; - + blockchainBridge.GetReceiptAndEffectiveGasPrice(Arg.Any()).Returns((receipt, UInt256.One, 0)); blockFinder.FindBlock(Arg.Any()).Returns(block); receiptFinder.Get(Arg.Any()).Returns(receiptsTab); @@ -599,8 +629,8 @@ public async Task Eth_get_transaction_receipt() Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"effectiveGasPrice\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]},{\"removed\":false,\"logIndex\":\"0x1\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"status\":\"0x1\",\"error\":\"error\",\"type\":\"0x0\"},\"id\":67}", serialized); } - - + + [Test] public async Task Eth_get_transaction_receipt_when_block_has_few_receipts() { @@ -608,7 +638,7 @@ public async Task Eth_get_transaction_receipt_when_block_has_few_receipts() IBlockchainBridge blockchainBridge = Substitute.For(); IBlockFinder blockFinder = Substitute.For(); IReceiptFinder receiptFinder = Substitute.For(); - + int blockNumber = 1; Block genesis = Build.A.Block.Genesis .WithStateRoot(new Keccak("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")) @@ -617,9 +647,9 @@ public async Task Eth_get_transaction_receipt_when_block_has_few_receipts() Block block = Build.A.Block.WithNumber(blockNumber).WithParent(previousBlock) .WithStateRoot(new Keccak("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")) .TestObject; - + LogEntry[] logEntries = new[] {Build.A.LogEntry.TestObject, Build.A.LogEntry.TestObject}; - + TxReceipt receipt1 = new TxReceipt() { Bloom = new Bloom(logEntries), @@ -635,7 +665,7 @@ public async Task Eth_get_transaction_receipt_when_block_has_few_receipts() GasUsedTotal = 2000, Logs = logEntries }; - + TxReceipt receipt2 = new TxReceipt() { Bloom = new Bloom(logEntries), @@ -651,11 +681,11 @@ public async Task Eth_get_transaction_receipt_when_block_has_few_receipts() GasUsedTotal = 2000, Logs = logEntries }; - + blockchainBridge.GetReceiptAndEffectiveGasPrice(Arg.Any()).Returns((receipt2, UInt256.One, 2)); - + TxReceipt[] receipts = {receipt1, receipt2}; - + blockFinder.FindBestSuggestedHeader().Returns(Build.A.BlockHeader.WithNumber(blockNumber).TestObject); blockFinder.Head.Returns(Build.A.Block.WithHeader(Build.A.BlockHeader.WithNumber(blockNumber).TestObject).TestObject); blockFinder.FindBlock(Arg.Any()).Returns(block); @@ -675,8 +705,8 @@ public async Task Eth_get_transaction_receipt_returns_null_on_missing_receipt() string serialized = ctx.Test.TestEthRpc("eth_getTransactionReceipt", TestItem.KeccakA.ToString()); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":67}", serialized); } - - + + [Test] public async Task Eth_getTransactionReceipt_return_info_about_mined_tx() { @@ -684,38 +714,38 @@ public async Task Eth_getTransactionReceipt_return_info_about_mined_tx() IBlockFinder blockFinder = Substitute.For(); IReceiptFinder receiptFinder = Substitute.For(); IBlockchainBridge blockchainBridge = Substitute.For(); - + await ctx.Test.AddFunds(new Address("0x723847c97bc651c7e8c013dbbe65a70712f02ad3"), 1.Ether()); Transaction tx = Build.A.Transaction.WithData(new byte[]{0, 1}) .SignedAndResolved().WithChainId(1).WithGasPrice(0).WithValue(0).WithGasLimit(210200).WithGasPrice(20.GWei()).TestObject; - + Block block = Build.A.Block.WithNumber(1) .WithStateRoot(new Keccak("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")) .WithTransactions(tx) .TestObject; - + await ctx.Test.AddBlock(tx); - + LogEntry[] entries = new[] { Build.A.LogEntry.TestObject, }; - + TxReceipt receipt = Build.A.Receipt.WithBloom(new Bloom(entries, new Bloom())).WithAllFieldsFilled .WithLogs(entries).TestObject; TxReceipt[] receiptsTab = {receipt}; - + blockFinder.FindBlock(Arg.Any()).Returns(block); receiptFinder.Get(Arg.Any()).Returns(receiptsTab); receiptFinder.Get(Arg.Any()).Returns(receiptsTab); blockchainBridge.GetReceiptAndEffectiveGasPrice(Arg.Any()).Returns((receipt, UInt256.One, 0)); - + ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockFinder(blockFinder).WithReceiptFinder(receiptFinder).WithBlockchainBridge(blockchainBridge).Build(); string serialized = ctx.Test.TestEthRpc("eth_getTransactionReceipt", tx.Hash!.ToString()); - + Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0xda6b4df2595675cbee0d4889f41c3d0790204e8ed1b8ad4cadaa45a7d50dace5\",\"transactionIndex\":\"0x2\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"cumulativeGasUsed\":\"0x3e8\",\"gasUsed\":\"0x64\",\"effectiveGasPrice\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"contractAddress\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x2\",\"transactionHash\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockHash\":\"0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72\",\"blockNumber\":\"0x2\",\"address\":\"0x0000000000000000000000000000000000000000\",\"data\":\"0x\",\"topics\":[\"0x0000000000000000000000000000000000000000000000000000000000000000\"]}],\"logsBloom\":\"0x00000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000\",\"root\":\"0x1f675bff07515f5df96737194ea945c36c41e7b4fcef307b7cd4d0e602a69111\",\"status\":\"0x1\",\"error\":\"error\",\"type\":\"0x0\"},\"id\":67}", serialized); } - + [Test] [Ignore("This test is flaky on CI. It could be connected with timeouts in block production.")] public async Task Eth_getTransactionReceipt_return_info_about_mined_1559tx() @@ -729,7 +759,7 @@ public async Task Eth_getTransactionReceipt_return_info_about_mined_1559tx() string serialized = ctx.Test.TestEthRpc("eth_getTransactionReceipt", tx.Hash!.ToString()); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":{\"transactionHash\":\"0x31501f80bf2ec493c368a519cb8ed6f132f0be26202304bbf1e1728642affb7f\",\"transactionIndex\":\"0x0\",\"blockHash\":\"0x54515a11aa6c392ee2e1071fca3a579bc9a520930ef757dbf9b7d85fe155c691\",\"blockNumber\":\"0x5\",\"cumulativeGasUsed\":\"0x521c\",\"gasUsed\":\"0x521c\",\"effectiveGasPrice\":\"0x5e91eb5d\",\"from\":\"0x723847c97bc651c7e8c013dbbe65a70712f02ad3\",\"to\":\"0x0000000000000000000000000000000000000000\",\"contractAddress\":null,\"logs\":[],\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"status\":\"0x1\",\"type\":\"0x2\"},\"id\":67}", serialized); } - + [Test] [Ignore("This test is flaky on CI. It could be connected with timeouts in block production.")] public async Task Eth_getTransactionByHash_return_info_about_mined_1559tx() @@ -777,11 +807,11 @@ public async Task Send_transaction_with_signature_will_not_try_to_sign() ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockchainBridge(bridge).WithTxSender(txSender).Build(); Transaction tx = Build.A.Transaction.Signed(new EthereumEcdsa(ChainId.Mainnet, LimboLogs.Instance), TestItem.PrivateKeyA).TestObject; string serialized = ctx.Test.TestEthRpc("eth_sendRawTransaction", Rlp.Encode(tx, RlpBehaviors.None).Bytes.ToHexString()); - + await txSender.Received().SendTransaction(Arg.Any(), TxHandlingOptions.PersistentBroadcast); Assert.AreEqual($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}", serialized); } - + [TestCase("01f85b821e8e8204d7847735940083030d408080853a60005500c080a0f43e70c79190701347517e283ef63753f6143a5225cbb500b14d98eadfb7616ba070893923d8a1fc97499f426524f9e82f8e0322dfac7c3d7e8a9eee515f0bcdc4")] public async Task Send_raw_transaction_will_send_transaction(string rawTransaction) { @@ -792,11 +822,11 @@ public async Task Send_raw_transaction_will_send_transaction(string rawTransacti ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockchainBridge(bridge).WithTxSender(txSender).Build(); string serialized = ctx.Test.TestEthRpc("eth_sendRawTransaction", rawTransaction); - + await txSender.Received().SendTransaction(Arg.Any(), TxHandlingOptions.PersistentBroadcast); Assert.AreEqual($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}", serialized); } - + [Test] public async Task Send_transaction_without_signature_will_not_set_nonce_when_zero_and_not_null() { @@ -816,7 +846,7 @@ public async Task Send_transaction_without_signature_will_not_set_nonce_when_zer await txSender.Received().SendTransaction(Arg.Any(), TxHandlingOptions.PersistentBroadcast); Assert.AreEqual($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}", serialized); } - + [Test] public async Task Send_transaction_without_signature_will_manage_nonce_when_null() { @@ -832,7 +862,7 @@ public async Task Send_transaction_without_signature_will_manage_nonce_when_null TransactionForRpc rpcTx = new(tx); rpcTx.Nonce = null; string serialized = ctx.Test.TestEthRpc("eth_sendTransaction", new EthereumJsonSerializer().Serialize(rpcTx)); - + await txSender.Received().SendTransaction(Arg.Any(), TxHandlingOptions.PersistentBroadcast | TxHandlingOptions.ManagedNonce); Assert.AreEqual($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}", serialized); } @@ -843,7 +873,7 @@ public async Task Send_transaction_should_return_ErrorCode_if_tx_not_added() using Context ctx = await Context.Create(); Transaction tx = Build.A.Transaction.WithValue(10000).SignedAndResolved(new PrivateKey("0x0000000000000000000000000000000000000000000000000000000000000001")).WithNonce(0).TestObject; TransactionForRpc txForRpc = new(tx); - + string serialized = ctx.Test.TestEthRpc("eth_sendTransaction", new EthereumJsonSerializer().Serialize(txForRpc)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32010,\"message\":\"InsufficientFunds, Account balance: 0, cumulative cost: 31000\"},\"id\":67}", serialized); @@ -855,11 +885,11 @@ public enum AccessListProvided Partial, Full } - + [TestCase(AccessListProvided.None, false, 2, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\"]},{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0xf71b\"},\"id\":67}")] [TestCase(AccessListProvided.Full, false, 2, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\"]},{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0xf71b\"},\"id\":67}")] [TestCase(AccessListProvided.Partial, false, 2, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]},{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\"]}],\"gasUsed\":\"0xf71b\"},\"id\":67}")] - + [TestCase(AccessListProvided.None, true, 2, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0xee83\"},\"id\":67}")] [TestCase(AccessListProvided.Full, true, 2, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0xee83\"},\"id\":67}")] [TestCase(AccessListProvided.Partial, true, 2, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0xee83\"},\"id\":67}")] @@ -867,15 +897,15 @@ public enum AccessListProvided [TestCase(AccessListProvided.None, true, AccessTxTracer.MaxStorageAccessToOptimize, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0x14289\"},\"id\":67}")] [TestCase(AccessListProvided.Full, true, AccessTxTracer.MaxStorageAccessToOptimize, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0x14289\"},\"id\":67}")] [TestCase(AccessListProvided.Partial, true, AccessTxTracer.MaxStorageAccessToOptimize, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0x14289\"},\"id\":67}")] - + [TestCase(AccessListProvided.None, true, AccessTxTracer.MaxStorageAccessToOptimize + 5, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\",\"0x0000000000000000000000000000000000000000000000000000000000000003\",\"0x0000000000000000000000000000000000000000000000000000000000000004\",\"0x0000000000000000000000000000000000000000000000000000000000000005\",\"0x0000000000000000000000000000000000000000000000000000000000000006\",\"0x0000000000000000000000000000000000000000000000000000000000000007\",\"0x0000000000000000000000000000000000000000000000000000000000000008\",\"0x0000000000000000000000000000000000000000000000000000000000000009\",\"0x000000000000000000000000000000000000000000000000000000000000000a\",\"0x000000000000000000000000000000000000000000000000000000000000000b\",\"0x000000000000000000000000000000000000000000000000000000000000000c\",\"0x000000000000000000000000000000000000000000000000000000000000000d\",\"0x000000000000000000000000000000000000000000000000000000000000000e\",\"0x000000000000000000000000000000000000000000000000000000000000000f\",\"0x0000000000000000000000000000000000000000000000000000000000000010\",\"0x0000000000000000000000000000000000000000000000000000000000000011\"]},{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0x16f48\"},\"id\":67}")] [TestCase(AccessListProvided.Full, true, AccessTxTracer.MaxStorageAccessToOptimize + 5, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\",\"0x0000000000000000000000000000000000000000000000000000000000000003\",\"0x0000000000000000000000000000000000000000000000000000000000000004\",\"0x0000000000000000000000000000000000000000000000000000000000000005\",\"0x0000000000000000000000000000000000000000000000000000000000000006\",\"0x0000000000000000000000000000000000000000000000000000000000000007\",\"0x0000000000000000000000000000000000000000000000000000000000000008\",\"0x0000000000000000000000000000000000000000000000000000000000000009\",\"0x000000000000000000000000000000000000000000000000000000000000000a\",\"0x000000000000000000000000000000000000000000000000000000000000000b\",\"0x000000000000000000000000000000000000000000000000000000000000000c\",\"0x000000000000000000000000000000000000000000000000000000000000000d\",\"0x000000000000000000000000000000000000000000000000000000000000000e\",\"0x000000000000000000000000000000000000000000000000000000000000000f\",\"0x0000000000000000000000000000000000000000000000000000000000000010\",\"0x0000000000000000000000000000000000000000000000000000000000000011\"]},{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0x16f48\"},\"id\":67}")] - [TestCase(AccessListProvided.Partial, true, AccessTxTracer.MaxStorageAccessToOptimize + 5, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]},{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\",\"0x0000000000000000000000000000000000000000000000000000000000000003\",\"0x0000000000000000000000000000000000000000000000000000000000000004\",\"0x0000000000000000000000000000000000000000000000000000000000000005\",\"0x0000000000000000000000000000000000000000000000000000000000000006\",\"0x0000000000000000000000000000000000000000000000000000000000000007\",\"0x0000000000000000000000000000000000000000000000000000000000000008\",\"0x0000000000000000000000000000000000000000000000000000000000000009\",\"0x000000000000000000000000000000000000000000000000000000000000000a\",\"0x000000000000000000000000000000000000000000000000000000000000000b\",\"0x000000000000000000000000000000000000000000000000000000000000000c\",\"0x000000000000000000000000000000000000000000000000000000000000000d\",\"0x000000000000000000000000000000000000000000000000000000000000000e\",\"0x000000000000000000000000000000000000000000000000000000000000000f\",\"0x0000000000000000000000000000000000000000000000000000000000000010\",\"0x0000000000000000000000000000000000000000000000000000000000000011\"]}],\"gasUsed\":\"0x16f48\"},\"id\":67}")] + [TestCase(AccessListProvided.Partial, true, AccessTxTracer.MaxStorageAccessToOptimize + 5, "{\"jsonrpc\":\"2.0\",\"result\":{\"accessList\":[{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]},{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\",\"0x0000000000000000000000000000000000000000000000000000000000000003\",\"0x0000000000000000000000000000000000000000000000000000000000000004\",\"0x0000000000000000000000000000000000000000000000000000000000000005\",\"0x0000000000000000000000000000000000000000000000000000000000000006\",\"0x0000000000000000000000000000000000000000000000000000000000000007\",\"0x0000000000000000000000000000000000000000000000000000000000000008\",\"0x0000000000000000000000000000000000000000000000000000000000000009\",\"0x000000000000000000000000000000000000000000000000000000000000000a\",\"0x000000000000000000000000000000000000000000000000000000000000000b\",\"0x000000000000000000000000000000000000000000000000000000000000000c\",\"0x000000000000000000000000000000000000000000000000000000000000000d\",\"0x000000000000000000000000000000000000000000000000000000000000000e\",\"0x000000000000000000000000000000000000000000000000000000000000000f\",\"0x0000000000000000000000000000000000000000000000000000000000000010\",\"0x0000000000000000000000000000000000000000000000000000000000000011\"]}],\"gasUsed\":\"0x16f48\"},\"id\":67}")] public async Task Eth_create_access_list_sample(AccessListProvided accessListProvided, bool optimize, long loads, string expected) { TestRpcBlockchain test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).Build(new TestSpecProvider(Berlin.Instance)); - + (byte[] code, AccessListItemForRpc[] _) = GetTestAccessList(loads); TransactionForRpc transaction = test.JsonSerializer.Deserialize($"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); @@ -884,7 +914,7 @@ public async Task Eth_create_access_list_sample(AccessListProvided accessListPro { transaction.AccessList = GetTestAccessList(2, accessListProvided == AccessListProvided.Full).AccessList; } - + string serialized = test.TestEthRpc("eth_createAccessList", test.JsonSerializer.Serialize(transaction), "0x0", optimize.ToString().ToLower()); Assert.AreEqual(expected, serialized); } @@ -899,7 +929,7 @@ public static void Should_handle_gasCap_as_max_if_null_or_zero(long? gasCap) Assert.AreEqual(long.MaxValue, rpcTx.Gas, "Gas must be set to max if gasCap is null or 0"); } - + private static (byte[] ByteCode, AccessListItemForRpc[] AccessList) GetTestAccessList(long loads = 2, bool allowSystemUser = true) { AccessListItemForRpc[] accessList = allowSystemUser @@ -910,7 +940,7 @@ private static (byte[] ByteCode, AccessListItemForRpc[] AccessList) GetTestAcces : new[] {new AccessListItemForRpc(TestItem.AddressC, Array.Empty())}; Prepare code = Prepare.EvmCode; - + for (int i = 1; i <= loads; i++) { // accesses Address.SystemUser with storage @@ -940,18 +970,18 @@ protected class Context : IDisposable public TestRpcBlockchain AuraTest { get; set; } = null!; private Context() { } - + public static async Task CreateWithLondonEnabled() { OverridableReleaseSpec releaseSpec = new(London.Instance) { Eip1559TransitionBlock = 1 }; TestSpecProvider specProvider = new(releaseSpec) {ChainId = ChainId.Mainnet}; return await Create(specProvider); } - + public static async Task Create(ISpecProvider? specProvider = null) => new() { - Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).Build(specProvider), + Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).Build(specProvider), AuraTest = await TestRpcBlockchain.ForTest(SealEngineType.AuRa).Build(specProvider) }; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs index 0504f2e5a3e..aa82e74ae7c 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -139,7 +139,7 @@ public ResultWrapper
eth_coinbase() { return ResultWrapper.Success(_gasPriceOracle.GetGasPriceEstimate()); } - + public ResultWrapper eth_maxPriorityFeePerGas() { UInt256 gasPriceWithBaseFee = _gasPriceOracle.GetMaxPriorityGasFeeEstimate(); @@ -210,25 +210,33 @@ public ResultWrapper eth_getStorageAt(Address address, UInt256 positionI return ResultWrapper.Success(storage.PadLeft(32)); } - public Task> eth_getTransactionCount(Address address, BlockParameter blockParameter) + public Task> eth_getTransactionCount(Address address, BlockParameter blockParameter) { + + if (blockParameter == BlockParameter.Pending) + { + UInt256 pendingNonce = _txPoolBridge.GetLatestPendingNonce(address); + return Task.FromResult(ResultWrapper.Success(pendingNonce)); + + } + SearchResult searchResult = _blockFinder.SearchForHeader(blockParameter); if (searchResult.IsError) { - return Task.FromResult(ResultWrapper.Fail(searchResult)); + return Task.FromResult(ResultWrapper.Fail(searchResult)); } BlockHeader header = searchResult.Object; if (!HasStateForBlock(_blockchainBridge, header)) { - return Task.FromResult(ResultWrapper.Fail($"No state available for block {header.Hash}", + return Task.FromResult(ResultWrapper.Fail($"No state available for block {header.Hash}", ErrorCodes.ResourceUnavailable)); } Account account = _stateReader.GetAccount(header.StateRoot, address); UInt256 nonce = account?.Nonce ?? 0; - return Task.FromResult(ResultWrapper.Success(nonce)); + return Task.FromResult(ResultWrapper.Success(nonce)); } public ResultWrapper eth_getBlockTransactionCountByHash(Keccak blockHash) @@ -503,7 +511,7 @@ public Task> eth_getTransactionReceipt(Keccak txHas { return Task.FromResult(ResultWrapper.Success(null)); } - + if (_logger.IsTrace) _logger.Trace($"eth_getTransactionReceipt request {txHash}, result: {txHash}"); return Task.FromResult(ResultWrapper.Success(new(txHash, receipt, effectiveGasPrice, logIndexStart))); } @@ -644,7 +652,7 @@ IEnumerable GetLogs(IEnumerable logs, CancellationTokenSou cancellationToken.ThrowIfCancellationRequested(); fromBlockResult = _blockFinder.SearchForHeader(filter.FromBlock); - } + } if (fromBlockResult.IsError) { @@ -693,7 +701,7 @@ public ResultWrapper> eth_getWork() return ResultWrapper.Fail("eth_submitHashrate not supported", ErrorCodes.MethodNotFound, null); } - // https://github.com/ethereum/EIPs/issues/1186 + // https://github.com/ethereum/EIPs/issues/1186 public ResultWrapper eth_getProof(Address accountAddress, byte[][] storageKeys, BlockParameter blockParameter) { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModuleProxy.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModuleProxy.cs index d381eb03038..efd376a9647 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModuleProxy.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModuleProxy.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -84,7 +84,7 @@ public ResultWrapper eth_snapshot() { throw new NotSupportedException(); } - + public ResultWrapper eth_maxPriorityFeePerGas() { throw new NotSupportedException(); @@ -112,8 +112,8 @@ public ResultWrapper eth_getStorageAt(Address address, UInt256 positionI throw new NotSupportedException(); } - public async Task> eth_getTransactionCount(Address address, BlockParameter blockParameter) - => ResultWrapper.From(await _proxy.eth_getTransactionCount(address, MapBlockParameter(blockParameter))); + public async Task> eth_getTransactionCount(Address address, BlockParameter blockParameter) + => ResultWrapper.From(await _proxy.eth_getTransactionCount(address, MapBlockParameter(blockParameter))); public ResultWrapper eth_getBlockTransactionCountByHash(Keccak blockHash) { @@ -152,9 +152,9 @@ public async Task> eth_sendTransaction(TransactionForRpc r { RpcResult chainIdResult = await _proxy.eth_chainId(); ulong chainId = chainIdResult?.IsValid == true ? (ulong)chainIdResult.Result : 0; - RpcResult nonceResult = + RpcResult nonceResult = await _proxy.eth_getTransactionCount(transaction.SenderAddress, BlockParameterModel.Pending); - transaction.Nonce = nonceResult?.IsValid == true ? nonceResult.Result ?? UInt256.Zero : UInt256.Zero; + transaction.Nonce = nonceResult?.IsValid == true ? nonceResult.Result : UInt256.Zero; _wallet.Sign(transaction, chainId); } @@ -317,7 +317,7 @@ public ResultWrapper eth_getProof(Address accountAddress, byte[][] Value = transaction.Value }; } - + private static ReceiptForRpc? MapReceipt(ReceiptModel? receipt) { if (receipt is null) diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs index 02aadc4cf03..66fdbeb960d 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -31,258 +31,258 @@ namespace Nethermind.JsonRpc.Modules.Eth [RpcModule(ModuleType.Eth)] public interface IEthRpcModule : IRpcModule { - [JsonRpcMethod(IsImplemented = true, - Description = "Returns ChainID", - IsSharable = true, + [JsonRpcMethod(IsImplemented = true, + Description = "Returns ChainID", + IsSharable = true, ExampleResponse = "0x4")] ResultWrapper eth_chainId(); - + [JsonRpcMethod(IsImplemented = true, - Description = "Returns ETH protocol version", - IsSharable = true, + Description = "Returns ETH protocol version", + IsSharable = true, ExampleResponse = "0x41")] ResultWrapper eth_protocolVersion(); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns syncing status", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns syncing status", + IsSharable = true, ExampleResponse = "{\"isSyncing\":true,\"startingBlock\":\"0x0\",\"currentBlock\":\"0x0\",\"highestBlock\":\"0x4df8a4\"},\"id\":1}")] ResultWrapper eth_syncing(); - - [JsonRpcMethod(IsImplemented = false, - Description = "Returns miner's coinbase", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = false, + Description = "Returns miner's coinbase", + IsSharable = true, ExampleResponse = "0x0000000000000000000000000000000000000000")] ResultWrapper
eth_coinbase(); - + [JsonRpcMethod(IsImplemented = false, Description = "Returns mining status", IsSharable = true)] ResultWrapper eth_mining(); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns block fee history.", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns block fee history.", + IsSharable = true, ExampleResponse = "{\"baseFeePerGas\": [\"0x116c1cbb03\", \"0x10c3714c06\"], \"gasUsedRatio\": [0.3487305666666667, 0.3], \"oldestBlock\": \"0xc7e5ff\", \"reward\": [[\"0x3b9aca00\",\"0x3b9aca00\"], [\"0x0\",\"0x3bb24dfa\"]]}")] ResultWrapper eth_feeHistory(long blockCount, BlockParameter newestBlock, double[]? rewardPercentiles = null); - + [JsonRpcMethod(IsImplemented = false, Description = "Returns full state snapshot", IsSharable = true)] ResultWrapper eth_snapshot(); - + [JsonRpcMethod(IsImplemented = false, Description = "", IsSharable = true)] ResultWrapper eth_maxPriorityFeePerGas(); - - [JsonRpcMethod(IsImplemented = false, - Description = "Returns mining hashrate", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = false, + Description = "Returns mining hashrate", + IsSharable = true, ExampleResponse = "0x0")] ResultWrapper eth_hashrate(); - - [JsonRpcMethod(IsImplemented = false, - Description = "Returns miner's gas price", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = false, + Description = "Returns miner's gas price", + IsSharable = true, ExampleResponse = "0x4a817c800" )] ResultWrapper eth_gasPrice(); - - [JsonRpcMethod(IsImplemented = false, - Description = "Returns accounts", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = false, + Description = "Returns accounts", + IsSharable = true, ExampleResponse = "[\"0x9b96a7841d6e0b76872c85c86082959189a27342\"]")] ResultWrapper> eth_accounts(); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns current block number", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns current block number", + IsSharable = true, ExampleResponse = "0x885480")] Task> eth_blockNumber(); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns account balance", + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns account balance", IsSharable = true, ExampleResponse = "0x6c8ae945bfe6e")] Task> eth_getBalance([JsonRpcParameter(ExampleValue = "[\"0x78467cada5f1883e79fcf0f3ebfa50abeec8c820\"]")] Address address, BlockParameter blockParameter = null); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns storage data at address. storage_index", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns storage data at address. storage_index", + IsSharable = true, ExampleResponse = "0x")] ResultWrapper eth_getStorageAt([JsonRpcParameter(ExampleValue = "[\"0x000000000000000000000000c666d239cbda32aa7ebca894b6dc598ddb881285\",\"0x2\"]")] Address address, UInt256 positionIndex, BlockParameter blockParameter = null); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns account nonce (number of trnsactions from the account since genesis) at the given block number", + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns account nonce (number of trnsactions from the account since genesis) at the given block number", IsSharable = true, ExampleResponse = "0x3e")] - Task> eth_getTransactionCount([JsonRpcParameter(ExampleValue = "[\"0xae3ed7a6ccdddf2914133d0669b5f02ff6fa8ad2\"]")] Address address, BlockParameter blockParameter = null); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns number of transactions in the block block hash", - IsSharable = true, + Task> eth_getTransactionCount([JsonRpcParameter(ExampleValue = "[\"0xae3ed7a6ccdddf2914133d0669b5f02ff6fa8ad2\"]")] Address address, BlockParameter blockParameter = null); + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns number of transactions in the block block hash", + IsSharable = true, ExampleResponse = "0x20")] ResultWrapper eth_getBlockTransactionCountByHash( [JsonRpcParameter(ExampleValue = "[\"0x199c2ef63392fb67f929fe0580e11f62fa6c54b9951a624896da91375a6805b1\"]")] Keccak blockHash); - - [JsonRpcMethod(IsImplemented = true, + + [JsonRpcMethod(IsImplemented = true, Description = "Returns number of transactions in the block by block number", IsSharable = true, ExampleResponse = "0x20")] ResultWrapper eth_getBlockTransactionCountByNumber([JsonRpcParameter(ExampleValue = "[\"8934677\"]")] BlockParameter blockParameter); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns number of uncles in the block by block hash", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns number of uncles in the block by block hash", + IsSharable = true, ExampleResponse = "0x0")] ResultWrapper eth_getUncleCountByBlockHash([JsonRpcParameter(ExampleValue = "[\"0xe495c3385bb9162103bc07989d7160c38759e017c37c7d0608268bd5989d6bed \"]")] Keccak blockHash); - - [JsonRpcMethod(IsImplemented = true, - Description = "Returns number of uncles in the block by block number", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = true, + Description = "Returns number of uncles in the block by block number", + IsSharable = true, ExampleResponse = "0x0")] ResultWrapper eth_getUncleCountByBlockNumber([JsonRpcParameter(ExampleValue = "[\"5127400\"]")] BlockParameter blockParameter); - + [JsonRpcMethod(IsImplemented = true, Description = "Returns account code at given address and block", IsSharable = true)] ResultWrapper eth_getCode(Address address, BlockParameter blockParameter = null); - + [JsonRpcMethod(IsImplemented = false, Description = "Signs a transaction", IsSharable = true)] ResultWrapper eth_sign(Address addressData, byte[] message); - - [JsonRpcMethod(IsImplemented = true, - Description = "Send a transaction to the tx pool and broadcasting", + + [JsonRpcMethod(IsImplemented = true, + Description = "Send a transaction to the tx pool and broadcasting", IsSharable = true, ExampleResponse = "0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760")] Task> eth_sendTransaction([JsonRpcParameter(ExampleValue = "[{\"From\": \"0xc2208fe87805279b03c1a8a78d7ee4bfdb0e48ee\", \"Gas\":\"21000\",\"GasPrice\":\"20000000000\", \"Nonce\":\"23794\", \"To\":\"0x2d44c0e097f6cd0f514edac633d82e01280b4a5c\"}]")] TransactionForRpc rpcTx); - - [JsonRpcMethod(IsImplemented = true, - Description = "Send a raw transaction to the tx pool and broadcasting", + + [JsonRpcMethod(IsImplemented = true, + Description = "Send a raw transaction to the tx pool and broadcasting", IsSharable = true, ExampleResponse = "0x7a5a94d5b5e3ce017ce2c2022f02ec5db10611c43695c3256861bdb19317ab0e" )] Task> eth_sendRawTransaction([JsonRpcParameter(ExampleValue = "[\"0xf86380843b9aca0082520894b943b13292086848d8180d75c73361107920bb1a80802ea0385656b91b8f1f5139e9ba3449b946a446c9cfe7adb91b180ddc22c33b17ac4da01fe821879d386b140fd8080dcaaa98b8c709c5025c8c4dea1334609ebac41b6c\"]")] byte[] transaction); - - [JsonRpcMethod(IsImplemented = true, - Description = "Executes a tx call (does not create a transaction)", + + [JsonRpcMethod(IsImplemented = true, + Description = "Executes a tx call (does not create a transaction)", IsSharable = false, ExampleResponse = "0x")] ResultWrapper eth_call([JsonRpcParameter(ExampleValue = "[{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}]")] TransactionForRpc transactionCall, BlockParameter? blockParameter = null); - - [JsonRpcMethod(IsImplemented = true, - Description = "Executes a tx call and returns gas used (does not create a transaction)", + + [JsonRpcMethod(IsImplemented = true, + Description = "Executes a tx call and returns gas used (does not create a transaction)", IsSharable = false, ExampleResponse = "0x")] ResultWrapper eth_estimateGas([JsonRpcParameter(ExampleValue = "[\"{\"from\": \"0x0001020304050607080910111213141516171819\", \"gasPrice\": \"1048576\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}\"]")] TransactionForRpc transactionCall, BlockParameter? blockParameter = null); - + [JsonRpcMethod(IsImplemented = true, Description = "Creates an [EIP2930](https://eips.ethereum.org/EIPS/eip-2930) type AccessList for the given transaction", EdgeCaseHint = "If your transaction has code executed, then you can generate transaction access list with eth_createAccessList. If you send it with your transaction then it will lower your gas cost on Ethereum", IsSharable = false, ExampleResponse = "{\"accessList\":[{\"address\":\"0xfffffffffffffffffffffffffffffffffffffffe\",\"storageKeys\":[\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"0x0000000000000000000000000000000000000000000000000000000000000002\"]},{\"address\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"storageKeys\":[]}],\"gasUsed\":\"0xf71b\"}")] - ResultWrapper eth_createAccessList( + ResultWrapper eth_createAccessList( [JsonRpcParameter(Description = "Transaction's details", ExampleValue = "[\"{\"type\":\"0x1\"]")] TransactionForRpc transactionCall, [JsonRpcParameter(Description = "(optional)")] BlockParameter? blockParameter = null, [JsonRpcParameter(Description = "(optional)")] bool optimize = true); - - [JsonRpcMethod(IsImplemented = true, - Description = "Retrieves a block by hash", + + [JsonRpcMethod(IsImplemented = true, + Description = "Retrieves a block by hash", IsSharable = true, ExampleResponse = "{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0x1\",\"extraData\":\"0x000000000000436f6e73656e5379732048797065726c656467657220426573754d3f7b71165a8266fcc569c96b6fcf9971ee4a8df59eeec4dcced0df8d778733429988e21d0124918859f988be9debf4b25fb5282ea41a2fc15f827f446ec93200\",\"gasLimit\":\"0x1c9c364\",\"gasUsed\":\"0x3aa87\",\"hash\":\"0xf33507f93a046dbdbb80dee5f47b84283297f6c53f1b665adc3cb6fe4138aa84\",\"logsBloom\":\"0x00000000000020000000000008000060000000000000000000000000000000000000000000000000201000020008000000000000000000000100000000200020000000000000000000000008000000000000000010000000000000000000000000000000000000000000080000000000000000000000002000000010000000000000000000000000000000000000000000040000001000000000000000020000020400000000000000000000000000000000000000000000000000010000000000000002080000000000000000020000000000000000000000000000000000000010020000000000000000000000000100000000000000000000010000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x0000000000000000\",\"number\":\"0x4e3d79\",\"parentHash\":\"0x01dba3a7eb61dc6dba3f9663c8fb632f76f60a476f57df74c3e5bd9d0a246339\",\"receiptsRoot\":\"0x70f3bd929735d8edeb953cd30a27e703e7dd3ec4af32cb74fe8ac302f9e7fb87\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x754\",\"stateRoot\":\"0x71af7e25302d1baa4c988c267450eb2c7fa20938fac377809c8d77f8ff8108ac\",\"totalDifficulty\":\"0x726275\",\"timestamp\":\"0x60ec1218\",\"baseFeePerGas\":\"0x7\",\"transactions\":[\"0xa65d391d8149ed0906fab923e870d2bc7f6d27c2be10fe1bcfc6f02869b38ef3\",\"0x369a89354041b7a8cb40edce51c36ebb0ee6ffa4d8056f5a658d90f3bbe1a81a\",\"0xf857daf60d03381b9a6ecb341b62798b424d20dc05763858e13955dd866b489d\"],\"transactionsRoot\":\"0x90115f8dc10c08e748675f52f3904615729a014461ca80d72c60239bf75ee209\",\"uncles\":[]}")] ResultWrapper eth_getBlockByHash([JsonRpcParameter(ExampleValue = "[\"0xf33507f93a046dbdbb80dee5f47b84283297f6c53f1b665adc3cb6fe4138aa84\"]")] Keccak blockHash, bool returnFullTransactionObjects = false); - - [JsonRpcMethod(IsImplemented = true, - Description = "Retrieves a block by number", + + [JsonRpcMethod(IsImplemented = true, + Description = "Retrieves a block by number", IsSharable = true, ExampleResponse = "{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0x1\",\"extraData\":\"0x000000000000436f6e73656e5379732048797065726c656467657220426573754d3f7b71165a8266fcc569c96b6fcf9971ee4a8df59eeec4dcced0df8d778733429988e21d0124918859f988be9debf4b25fb5282ea41a2fc15f827f446ec93200\",\"gasLimit\":\"0x1c9c364\",\"gasUsed\":\"0x3aa87\",\"hash\":\"0xf33507f93a046dbdbb80dee5f47b84283297f6c53f1b665adc3cb6fe4138aa84\",\"logsBloom\":\"0x00000000000020000000000008000060000000000000000000000000000000000000000000000000201000020008000000000000000000000100000000200020000000000000000000000008000000000000000010000000000000000000000000000000000000000000080000000000000000000000002000000010000000000000000000000000000000000000000000040000001000000000000000020000020400000000000000000000000000000000000000000000000000010000000000000002080000000000000000020000000000000000000000000000000000000010020000000000000000000000000100000000000000000000010000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x0000000000000000\",\"number\":\"0x4e3d79\",\"parentHash\":\"0x01dba3a7eb61dc6dba3f9663c8fb632f76f60a476f57df74c3e5bd9d0a246339\",\"receiptsRoot\":\"0x70f3bd929735d8edeb953cd30a27e703e7dd3ec4af32cb74fe8ac302f9e7fb87\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x754\",\"stateRoot\":\"0x71af7e25302d1baa4c988c267450eb2c7fa20938fac377809c8d77f8ff8108ac\",\"totalDifficulty\":\"0x726275\",\"timestamp\":\"0x60ec1218\",\"baseFeePerGas\":\"0x7\",\"transactions\":[\"0xa65d391d8149ed0906fab923e870d2bc7f6d27c2be10fe1bcfc6f02869b38ef3\",\"0x369a89354041b7a8cb40edce51c36ebb0ee6ffa4d8056f5a658d90f3bbe1a81a\",\"0xf857daf60d03381b9a6ecb341b62798b424d20dc05763858e13955dd866b489d\"],\"transactionsRoot\":\"0x90115f8dc10c08e748675f52f3904615729a014461ca80d72c60239bf75ee209\",\"uncles\":[]}")] ResultWrapper eth_getBlockByNumber([JsonRpcParameter(ExampleValue = "[\"5127545\"]")] BlockParameter blockParameter, bool returnFullTransactionObjects = false); - - [JsonRpcMethod(IsImplemented = true, - Description = "Retrieves a transaction by hash", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = true, + Description = "Retrieves a transaction by hash", + IsSharable = true, ExampleResponse = "{\"hash\":\"0xabca23910646013d608ec671de099447ab60b2b7159ad8319c3c088e8d9ea0fa\",\"nonce\":\"0x1a\",\"blockHash\":\"0xcb6756f69e0469acd5e5bb77966be580786ec2c11de85c9ddfd75257010e34f8\",\"blockNumber\":\"0x4dfbc7\",\"transactionIndex\":\"0xb\",\"from\":\"0xe1e7ab1c643dbe5b24739fdf2a5c7c193b54dd99\",\"to\":\"0x0b10e304088b2ba2b2acfd2f72573faad31a13a5\",\"value\":\"0x0\",\"gasPrice\":\"0x2540be400\",\"gas\":\"0xb4a4\",\"data\":\"0x095ea7b300000000000000000000000092c1576845703089cf6c0788379ed81f75f45dd500000000000000000000000000000000000000000000000000000002540be400\",\"input\":\"0x095ea7b300000000000000000000000092c1576845703089cf6c0788379ed81f75f45dd500000000000000000000000000000000000000000000000000000002540be400\",\"type\":\"0x0\",\"v\":\"0x2d\",\"s\":\"0x496d72d435ead8a8a9a865b14d6a102c1a9f848681d050dbbf11c522c612235\",\"r\":\"0xc8350e831203fecc8bff41f5cf858ac1d121e4b4d9e59c1137cc9440516ca9fd\"}")] Task> eth_getTransactionByHash( [JsonRpcParameter(ExampleValue = "\"0xabca23910646013d608ec671de099447ab60b2b7159ad8319c3c088e8d9ea0fa\"")] Keccak transactionHash); - - [JsonRpcMethod(IsImplemented = true, + + [JsonRpcMethod(IsImplemented = true, Description = "Returns the pending transactions list", - IsSharable = true, + IsSharable = true, ExampleResponse = "[]")] ResultWrapper eth_pendingTransactions(); - - [JsonRpcMethod(IsImplemented = true, - Description = "Retrieves a transaction by block hash and index", + + [JsonRpcMethod(IsImplemented = true, + Description = "Retrieves a transaction by block hash and index", IsSharable = true, ExampleResponse = "{\"hash\":\"0xb87ec4c8cb36a06f49cdd93c2e9f63e0b7db9af07a605c8bcf1fbe705162344e\",\"nonce\":\"0x5d\",\"blockHash\":\"0xfe47fb3539ccce9d19a032473effdd6ce19e3c921bbae2746152ccf82ceef48e\",\"blockNumber\":\"0x4dfc90\",\"transactionIndex\":\"0x2\",\"from\":\"0xaa9a0f962e433755c843175488fe088fccf8526f\",\"to\":\"0x074b24cef703f17fe123fa1b82081055775b7004\",\"value\":\"0x0\",\"gasPrice\":\"0x2540be401\",\"gas\":\"0x130ab\",\"data\":\"0x428dc451000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005d3c0f4ca5ee99f8e8f59ff9a5fab04f6a7e007f0000000000000000000000009d233a907e065855d2a9c7d4b552ea27fb2e5a36000000000000000000000000cbe56b00d173a26a5978ce90db2e33622fd95a28\",\"input\":\"0x428dc451000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005d3c0f4ca5ee99f8e8f59ff9a5fab04f6a7e007f0000000000000000000000009d233a907e065855d2a9c7d4b552ea27fb2e5a36000000000000000000000000cbe56b00d173a26a5978ce90db2e33622fd95a28\",\"type\":\"0x0\",\"v\":\"0x2e\",\"s\":\"0x696f6db060a6dd30435a7f592506ba3213f81cf4704e211a1a45a99f8984189a\",\"r\":\"0x7e07076186e38b68cb7e4f68a04258a5744c5a2ad1a7153456ee662a07902954\"}")] ResultWrapper eth_getTransactionByBlockHashAndIndex( [JsonRpcParameter(ExampleValue = "[\"0xfe47fb3539ccce9d19a032473effdd6ce19e3c921bbae2746152ccf82ceef48e\",\"0x2\"]")] Keccak blockHash, UInt256 positionIndex); - - [JsonRpcMethod(IsImplemented = true, - Description = "Retrieves a transaction by block number and index", - IsSharable = true, + + [JsonRpcMethod(IsImplemented = true, + Description = "Retrieves a transaction by block number and index", + IsSharable = true, ExampleResponse = "{\"hash\":\"0xfd320a4949990929f64b52041c58a74c8ce13289b3d6853bd8073b0580aa031a\",\"nonce\":\"0x5b\",\"blockHash\":\"0xd779e1a5ce8f34544d66d219bb3e5331a7b280fae89a36d7d52813a23e1ca1e3\",\"blockNumber\":\"0x4dfdd8\",\"transactionIndex\":\"0x8\",\"from\":\"0xadb540569e2db497bd973c141b0b63be98461e40\",\"to\":\"0x074b24cef703f17fe123fa1b82081055775b7004\",\"value\":\"0x0\",\"gasPrice\":\"0x12a05f200\",\"gas\":\"0x927c0\",\"data\":\"0x428dc451000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005d3c0f4ca5ee99f8e8f59ff9a5fab04f6a7e007f0000000000000000000000009d233a907e065855d2a9c7d4b552ea27fb2e5a36000000000000000000000000cbe56b00d173a26a5978ce90db2e33622fd95a28\",\"input\":\"0x428dc451000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005d3c0f4ca5ee99f8e8f59ff9a5fab04f6a7e007f0000000000000000000000009d233a907e065855d2a9c7d4b552ea27fb2e5a36000000000000000000000000cbe56b00d173a26a5978ce90db2e33622fd95a28\",\"type\":\"0x0\",\"v\":\"0x2e\",\"s\":\"0x37b90a929884787df717c87258f0434e2f115ce2fbb4bfc230322112fa9d5bbc\",\"r\":\"0x5222eff9e16b5c3e9e8901d9c45fc8e0f9cf774e8a56546a504025ef67ceefec\"}")] ResultWrapper eth_getTransactionByBlockNumberAndIndex( [JsonRpcParameter(ExampleValue = "[\"5111256\",\"0x8\"]" )] BlockParameter blockParameter, UInt256 positionIndex); - - [JsonRpcMethod(IsImplemented = true, - Description = "Retrieves a transaction receipt by tx hash", + + [JsonRpcMethod(IsImplemented = true, + Description = "Retrieves a transaction receipt by tx hash", IsSharable = true, ExampleResponse = "{\"transactionHash\":\"0x80757153e93d1b475e203406727b62a501187f63e23b8fa999279e219ee3be71\",\"transactionIndex\":\"0x7\",\"blockHash\":\"0x42def051b21038905cd2a2bc28d460a94df2249466847f0e1bcb4be4eb21891a\",\"blockNumber\":\"0x4e3f39\",\"cumulativeGasUsed\":\"0x62c9d\",\"gasUsed\":\"0xe384\",\"effectiveGasPrice\":\"0x12a05f200\",\"from\":\"0x0afe0a94415e8974052e7e6cfab19ee1c2ef4f69\",\"to\":\"0x19e8c84d4943e58b035626b064cfc76ee13ee6cb\",\"contractAddress\":null,\"logs\":[{\"removed\":false,\"logIndex\":\"0x0\",\"transactionIndex\":\"0x7\",\"transactionHash\":\"0x80757153e93d1b475e203406727b62a501187f63e23b8fa999279e219ee3be71\",\"blockHash\":\"0x42def051b21038905cd2a2bc28d460a94df2249466847f0e1bcb4be4eb21891a\",\"blockNumber\":\"0x4e3f39\",\"address\":\"0x2ac3c1d3e24b45c6c310534bc2dd84b5ed576335\",\"data\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"0x00000000000000000000000019e8c84d4943e58b035626b064cfc76ee13ee6cb\",\"0x00000000000000000000000028078300a459a9e136f872285654cdc74463041e\"]},{\"removed\":false,\"logIndex\":\"0x1\",\"transactionIndex\":\"0x7\",\"transactionHash\":\"0x80757153e93d1b475e203406727b62a501187f63e23b8fa999279e219ee3be71\",\"blockHash\":\"0x42def051b21038905cd2a2bc28d460a94df2249466847f0e1bcb4be4eb21891a\",\"blockNumber\":\"0x4e3f39\",\"address\":\"0x19e8c84d4943e58b035626b064cfc76ee13ee6cb\",\"data\":\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007735940000000000000000000000000000000000000000000000000000000000000000000\",\"topics\":[\"0x950494fc3642fae5221b6c32e0e45765c95ebb382a04a71b160db0843e74c99f\",\"0x0000000000000000000000000afe0a94415e8974052e7e6cfab19ee1c2ef4f69\",\"0x00000000000000000000000028078300a459a9e136f872285654cdc74463041e\",\"0x0000000000000000000000000afe0a94415e8974052e7e6cfab19ee1c2ef4f69\"]}],\"logsBloom\":\"0x00000000000000000000000000000000000000000000000020000000000000800000000000000000000400000000000000000000000000000000000000002000000000000000000000000008000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000812000000000000000000000000000001000000000000000000000008000400008000000000000000000000000000000000000000000000000000000000800000000000000000000002000000000000000000000000000000000000100000000000000000002000000000000000000000000010000000000000000000000400000000020000\",\"status\":\"0x1\",\"type\":\"0x0\"}")] Task> eth_getTransactionReceipt([JsonRpcParameter(ExampleValue = "[\"0x80757153e93d1b475e203406727b62a501187f63e23b8fa999279e219ee3be71\"]")] Keccak txHashData); - - [JsonRpcMethod(IsImplemented = true, - Description = "Retrieves an uncle block header by block hash and uncle index", + + [JsonRpcMethod(IsImplemented = true, + Description = "Retrieves an uncle block header by block hash and uncle index", IsSharable = true)] ResultWrapper eth_getUncleByBlockHashAndIndex(Keccak blockHashData, UInt256 positionIndex); - + [JsonRpcMethod(IsImplemented = true, Description = "Retrieves an uncle block header by block number and uncle index", IsSharable = true)] ResultWrapper eth_getUncleByBlockNumberAndIndex(BlockParameter blockParameter, UInt256 positionIndex); - - [JsonRpcMethod(IsImplemented = true, - Description = "Creates an update filter", + + [JsonRpcMethod(IsImplemented = true, + Description = "Creates an update filter", IsSharable = false, ExampleResponse = "0x9")] ResultWrapper eth_newFilter([JsonRpcParameter(ExampleValue = "[{\"toBlock\":\"latest\"}]" )] Filter filter); - - [JsonRpcMethod(IsImplemented = true, - Description = "Creates an update filter", - IsSharable = false, + + [JsonRpcMethod(IsImplemented = true, + Description = "Creates an update filter", + IsSharable = false, ExampleResponse = "0x0")] ResultWrapper eth_newBlockFilter(); - - [JsonRpcMethod(IsImplemented = true, - Description = "Creates an update filter", - IsSharable = false, + + [JsonRpcMethod(IsImplemented = true, + Description = "Creates an update filter", + IsSharable = false, ExampleResponse = "0x1")] ResultWrapper eth_newPendingTransactionFilter(); - - [JsonRpcMethod(IsImplemented = true, - Description = "Creates an update filter", + + [JsonRpcMethod(IsImplemented = true, + Description = "Creates an update filter", IsSharable = false)] ResultWrapper eth_uninstallFilter(UInt256 filterId); - - [JsonRpcMethod(IsImplemented = true, - Description = "Reads filter changes", + + [JsonRpcMethod(IsImplemented = true, + Description = "Reads filter changes", IsSharable = true, ExampleResponse = "[]")] ResultWrapper> eth_getFilterChanges([JsonRpcParameter(ExampleValue = "[\"0x9\"]" )] UInt256 filterId); - - [JsonRpcMethod(IsImplemented = true, - Description = "Reads filter changes", + + [JsonRpcMethod(IsImplemented = true, + Description = "Reads filter changes", IsSharable = true, ExampleResponse = "[]")] ResultWrapper> eth_getFilterLogs([JsonRpcParameter(ExampleValue = "[\"0x9\"]")] UInt256 filterId); - + [JsonRpcMethod(IsImplemented = true, Description = "Reads logs", IsSharable = false)] ResultWrapper> eth_getLogs(Filter filter); - + [JsonRpcMethod(Description = "", IsImplemented = false, IsSharable = true)] ResultWrapper> eth_getWork(); - + [JsonRpcMethod(Description = "", IsImplemented = false, IsSharable = false)] ResultWrapper eth_submitWork(byte[] nonce, Keccak headerPowHash, byte[] mixDigest); - + [JsonRpcMethod(Description = "", IsImplemented = false, IsSharable = false)] ResultWrapper eth_submitHashrate(string hashRate, string id); - - [JsonRpcMethod(Description = "https://github.com/ethereum/EIPs/issues/1186", - IsImplemented = true, + + [JsonRpcMethod(Description = "https://github.com/ethereum/EIPs/issues/1186", + IsImplemented = true, IsSharable = true, ExampleResponse = " \"accountProof\": [\"0xf90211a0446f43a2d3e433732c75bcf3519f4844e0441a4d39b31395ee9a65700c30d3b4a0b9720db63afe9909418fb6e02c9d9f225310856549cc1b66b486041f2d867250a046e6e560e52d4fe0d2f6609f489ba85f18ad1655fee18452588dc08388fbd711a01e68f36c91bd15cbf65587d6db2a7cbd6635907291e77dd80152161da9a28a48a0d2178a1891c26ccaa2d2cec82c231a0640a26a1f5e07c7b5493761bdb3aa94e5a0fa909327d406980a2e602eadd3f56cf8dc89320d4662340962e9cac2beee3d8da0a0fc71e7dec6320a993b4b65b2f82544910d0a4a7c6f8c5a1ebaa38357d259e3a0680161dec84c5f1c8d5e2a585c9708b1b6fbc2dc664a432e045d99f5e7d89259a0f76a745765be58d46d795c44d3900a4a05b6396530244d50822616c8bbb11e19a0594824352d58f5caff819c8df9581b6a41d0e94eb584ed0431d48b48f320bb5ca0e762eb52b2bcacd728fac605de6229dc83588001ecddcd3b454b64c393ee69eda0d319cf1021af0a8535e4916c3404c84917957d73d0711f71fd6456b4533993bba0878240238a894e6fa798671ac3792563c6666a7c7fba8066d090b65d6a7aa701a03c03fdb4d8f4b241442814cbab24ddb42b75c78874f92fedc162b65d0820fc4da06a3318509aa9ff009b9acb9b348f197a134a46a46295714f436d4fbb19057e69a04139df1b6e0a59b093b35f34f9e5e890bc06832e63b366d768dc29e8638b828480\",\"0xf90211a023459f17e04fba3d19c6993f5be413969842fdbdc85d234a91b2f6b08a38be87a0153060eafecbff55ef0794802ef722b6c66698141cdc0610352d3a426976adeba0bd642b7c5111a1fd09da33feb6df031dc352b6cb20fbbe5ebe3eb328db233bd4a0705bff29e05c7ef69c07fecaa5db2457b2f124befc82f9fe6b0e54c8e35632eba03c1b4ffc076434de97050d2351c24689cfaefaa6cf8dc398dd3b8ce365e652c1a0a1ebf845ea0eb252e2a2e5c422ccd74878a3733144dfd62bcaad34758cc98652a01e4184586f5bdbb17ba74fd87539f02378c7adcef99f1538108f9555520e32d6a0b8acdfd5b644fa2c9a54f68039a3af4c6562c1e7f91ea9e63bda5a849f1260b6a05c1f036a2e7a5829799fc7df2d87eac3e7aee55df461b040c36f5b5c61781059a0a67fd871d32642e44120331f76c2616096d04d7fa1a7db421bafbc39713d8bfba085c15b7ab64f61670f4422adb82176d5808fad4abde6fddda507b0e5ff92ba14a0d95e8f16a39d4e52c67c617eef486adcd947854373ac074ff498150c7ca1ab5da03d9d7be595000872ad6aec05667ad85e1aaaeb2050a692818d3e60d8f1628d8ba0984c657192b052d13fb717051631d67fbc83dd5dcb4d074a2fddc01aa122d95ba03643408862d758aea269c05027a1cd616c957e0db5daea529b56964db8b4f04ba01020dce8d692c3d84d9ae3e42c35e4d8adbddf7b4dd3e09e543fc980849f016e80\",\"0xf90211a04c71b4b56ed723da1c1353ec1b4c23e71dfa821664d4041c1ee1770221f507b6a031c851f261a38df9b2bece1a1cb6985bccfaa10d2bb15954b82cd2ceaad87032a08a4a3d0cc260cf0e0fef54490ce45796fdd3f522451976ca7834563c839c630fa003d074f79074566cd33a3d6a57b6ca8426ca9ea972f66b5dfde00f73287fcfcea07003d29a5bd192038600118ab5941af5c79c1f0fc6184ad564180b809c36c7c4a05f181c50402dcff567abe1c6679a8d5e3825125abca4d969c7cbf76503416813a06a85dfca80e442ef79b66162099d52eaf67718589eb794755ce57dc071a85cdaa085cba9e6937a8a5f0a7d1b5ee9eb9f03c40f89eb13d9d4e0e5fbc574c2b852faa063f93dce441a3373cfc2d1c855884682dfd8d09d1eb9844c73d88eb8d5a7cdfda0e4bc0d2597e5fd0a4cd5e76a03b657ef8959264bdeaf95c4412ebd4ff736ce44a00239290e698aa04485e0c342cfb76ccf27a3e45a161b8b1b534e0c46707b92c8a0080c3439fb84730924539797aad8d017c5f7e008314ed9086450d80ec2b0d7aba0861dbe37b9b9e0f58b6fdb83eec28045c5f7f1861530f47f78fc8a2b18a6bd8da0036697e8dc063e9086d115d468c934a01123adb3c66dcc236ee4aa8141888626a033c6f574ee79d9b1322e9ca1131a5984b33cc8881e6ac8ebd6ca36f3437cedcda07fc2855e6bb0f276202094dffe49f2b62f2366d9aba9db3ffe76d62bcdc29f0d80\",\"0xf90211a06995d919b53eefa0b097d75c2a5dee2c54109a06d3b60586327fa0086437b801a05c7d7c92f9f1e49cf27c5d97b4a96302f033d42df5b1d7c013ef05031d67e567a05278417d007913a1e7d6606fb464e7b81f6cee91b6a1d250c67b3822d9fc68d8a0fba6d9cd68fe72db07af9d99e30c32502e0afb15ee9712f6781014195444b9e1a07dca25ba23f429b5960d9feb23367e2a088e50211f280118b7f1703e6d47103fa0399eb6e0d4390688f6b28df56c7ad72d6b6cbac9066110c6a727fe35cd889e9da08ef84ddaa3b70095fb5624878289744740a9f8761ef1132ba722abc977a218ffa04296811ae184892e2d5ecc18d05fc6279d6168eb0f3abb1f97d8d0a0721c12fba05c46766c579b8a0b8a0b79b84f6cd1e5dae1c53a2988883b0385daa2cf3bdf82a01a4ba17dd1b59147a321dd374a22a0d959f1a79d70132db7f1a8b89968ff6062a0f7ffc6f3921c6bccd47c862519409e63f51d39aaa215819c664b1adb48a940b0a0dc6e636385407900a649917fb772b0972d50d197e9fd5cdb853a1c98a29e6a47a0674b224cf784c59ca937bfebbdcd8dec05ddbd57400b04f5965558a0c2d2299ca0f95ce8c921c5b17ebf74563f2496a88631aa6a697bfd9e3e22b326efa453115ea0fc133bc6b9dd098933c816660df2959074f47dfc4ab3a10fd2059a2b2e0e911aa057cac15218d6013890df78eec099144ba2000e3eea73a3498d0eb9b1f733459080\",\"0xf90211a0400aafe69a1a482277db720d12b9c0b98695f5dd78c6faf5421b3ddac50165a6a0235987542e4b37aa8e6957776c9dff11d6818797db5ad505de5e0049045c7e20a0f573b4776f8b323b7d55850300d53855cfa6fa5fe6e36ba64da6bb263fef774aa0b3a36d14d660c3492785b0f1488a2231b6d83bd51268685b95ba9267aa331fe2a0096e8c65bae8fce7d234710a1e1b8c98bd4fb2d56f8bb2eda7ef20d1cf31c7e2a059194c8bf50c2ac393c4c60a59c7ddf0c515bd9f545fc4ef212629a8b96af62aa0ffe882f4e2f1e8e49c7777f6f9b4438a9f31d4e5cefe82c96fdd3587d9a95173a00127ced7fdbdd57cd5ed8b766c9312c09e0c67a350087d22b4cc7b2d17a45479a0cfc030a250448838caa716cd2767cd1a4837b29019f474980720c94fe2ce412ea079ec358d2b4122692bf70eb73a0ddb0ff4bfeb05d503fe1acafe068e2d3d33cfa0733e2ccdc638ca3c940c566c742a1b9d58f7caaa062e8a121c07f5e3367160a8a0aa1f403798b71c67b821e6f6128cc5366bebe145ebd563714cf9972b2474814ea01b988afc628922aeed3de606a8a462093f1c0c803a563bbe178552a360bad1e1a0082741e2219024bf4e19f5b1b0643e5e885cb7dfb4cdc0a51faf5bd9f92ff9b6a03c86490fe8f0256be44b95815086d95cb62fdbc3ede63ca08d12d68f274b7fc5a03a81565e860ac32921ed4c9f4e0ace3b341c342abd030d4955f2d1e64dd81d2b80\",\"0xf8f1a0bd9a0d9559513a6c7bf427816142d254d5a9049e9ff385f3514b50cb828951fc808080a07d37353f509c9bdc99635bd75fde71a6ef99271154ac4ffd5c437e0b951d5eaca029e3beec2f52c99a1fa73251ed64486f2766af3dcb950900679f7fd740badfdaa09b348c93803521a41bd2a754e3ea5435bb2663724cdfb70a87984458b53f03dea0904e696aceac8c89e2825e0dae8add52a9b46faef2ffbabb932e8bc1267e48ba80a0935dedba6ec5fb5b89285993c5f1be0cb77492e63e11bb38b5aca18011699eb8a06b52f587932dfb669f6cbefe35b251c6d8e6b5e8a2e1c1a7c2a452a4f2917b0d808080808080\"],\"address\":\"0x7f0d15c7faae65896648c8273b6d7e43f58fa842\",\"balance\":\"0x0\",\"codeHash\":\"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\",\"nonce\":\"0x0\",\"storageHash\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"storageProof\":[{\"key\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"proof\":[],\"value\":\"0x00\"]")] ResultWrapper eth_getProof([JsonRpcParameter(ExampleValue = "[\"0x7F0d15C7FAae65896648C8273B6d7E43f58Fa842\",[ \"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\" ],\"latest\"]")] Address accountAddress, byte[][] hashRate, BlockParameter blockParameter); diff --git a/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs b/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs index c87d6aa8e21..71fa9e2c2ce 100644 --- a/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs +++ b/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs @@ -275,6 +275,53 @@ public void should_ignore_old_nonce_transactions() result.Should().Be(AcceptTxResult.OldNonce); } + [Test] + public void get_next_pending_nonce() + { + _txPool = CreatePool(); + + // LatestPendingNonce=0, when account does not exist + UInt256? latestNonce = _txPool.GetLatestPendingNonce(TestItem.AddressA); + + _stateProvider.CreateAccount(TestItem.AddressA, 10.Ether()); + + // LatestPendingNonce=0, for a new account + latestNonce = _txPool.GetLatestPendingNonce(TestItem.AddressA); + Assert.AreEqual(latestNonce, (UInt256)0); + + // LatestPendingNonce=1, when the current nonce of the account=1 and no pending transactions + _stateProvider.IncrementNonce(TestItem.AddressA); + latestNonce = _txPool.GetLatestPendingNonce(TestItem.AddressA); + Assert.AreEqual(latestNonce, (UInt256)1); + + // LatestPendingNonce=1, when a pending transaction added to the pool with a gap in nonce (skipping nonce=1) + Transaction tx = Build.A.Transaction.WithNonce(2).SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject; + AcceptTxResult result = _txPool.SubmitTx(tx, TxHandlingOptions.PersistentBroadcast); + result.Should().Be(AcceptTxResult.Accepted); + latestNonce = _txPool.GetLatestPendingNonce(TestItem.AddressA); + Assert.AreEqual(latestNonce, (UInt256)1); + + // LatestPendingNonce=5, when added pending transactions upto nonce=4 + tx = Build.A.Transaction.WithNonce(1).SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject; + result = _txPool.SubmitTx(tx, TxHandlingOptions.PersistentBroadcast); + result.Should().Be(AcceptTxResult.Accepted); + tx = Build.A.Transaction.WithNonce(3).SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject; + result = _txPool.SubmitTx(tx, TxHandlingOptions.PersistentBroadcast); + result.Should().Be(AcceptTxResult.Accepted); + tx = Build.A.Transaction.WithNonce(4).SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject; + result = _txPool.SubmitTx(tx, TxHandlingOptions.PersistentBroadcast); + result.Should().Be(AcceptTxResult.Accepted); + latestNonce = _txPool.GetLatestPendingNonce(TestItem.AddressA); + Assert.AreEqual(latestNonce, (UInt256)5); + + //LatestPendingNonce=5, when added a new pending transaction with a gap in nonce (skipped nonce=5) + tx = Build.A.Transaction.WithNonce(6).SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject; + result = _txPool.SubmitTx(tx, TxHandlingOptions.PersistentBroadcast); + result.Should().Be(AcceptTxResult.Accepted); + latestNonce = _txPool.GetLatestPendingNonce(TestItem.AddressA); + Assert.AreEqual(latestNonce, (UInt256)5); + } + [Test] public void should_ignore_overflow_transactions() { diff --git a/src/Nethermind/Nethermind.TxPool/Collections/SortedPool.cs b/src/Nethermind/Nethermind.TxPool/Collections/SortedPool.cs index b5d85b832d2..35c5327dcba 100644 --- a/src/Nethermind/Nethermind.TxPool/Collections/SortedPool.cs +++ b/src/Nethermind/Nethermind.TxPool/Collections/SortedPool.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -24,28 +24,28 @@ namespace Nethermind.TxPool.Collections { /// - /// Keeps a pool of with in groups based on . + /// Keeps a pool of with in groups based on . /// /// Type of keys of items, unique in pool. /// Type of items that are kept. /// Type of groups in which the items are organized - public abstract partial class SortedPool + public abstract partial class SortedPool where TKey : notnull where TGroupKey : notnull { private readonly int _capacity; - + // comparer for a bucket private readonly IComparer _groupComparer; - + // group buckets, keep the items grouped by group key and sorted in group - protected readonly IDictionary> _buckets; - + private readonly IDictionary> _buckets; + private readonly IDictionary _cacheMap; - + // comparer for worst elements in buckets private readonly IComparer _sortedComparer; - + // worst element from every group, used to determine element that will be evicted when pool is full protected readonly DictionarySortedSet _worstSortedValues; @@ -61,7 +61,7 @@ protected SortedPool(int capacity, IComparer comparer) _sortedComparer = GetUniqueComparer(comparer ?? throw new ArgumentNullException(nameof(comparer))); _groupComparer = GetGroupComparer(comparer ?? throw new ArgumentNullException(nameof(comparer))); _cacheMap = new Dictionary(); // do not initialize it at the full capacity - _buckets = new Dictionary>(); + _buckets = new Dictionary>(); _worstSortedValues = new DictionarySortedSet(_sortedComparer); } @@ -71,14 +71,14 @@ protected SortedPool(int capacity, IComparer comparer) /// Original comparer. /// Identity comparer. protected abstract IComparer GetUniqueComparer(IComparer comparer); - + /// /// Gets comparer for same group. /// /// Original comparer. /// Group comparer. protected abstract IComparer GetGroupComparer(IComparer comparer); - + /// /// Maps item to group /// @@ -87,7 +87,7 @@ protected SortedPool(int capacity, IComparer comparer) protected abstract TGroupKey MapToGroup(TValue value); public int Count => _cacheMap.Count; - + /// /// Gets all items in random order. /// @@ -96,14 +96,14 @@ public TValue[] GetSnapshot() { return _buckets.SelectMany(b => b.Value).ToArray(); } - + /// /// Gets all items in groups in supplied comparer order in groups. /// [MethodImpl(MethodImplOptions.Synchronized)] public IDictionary GetBucketSnapshot(Predicate? where = null) { - IEnumerable>> buckets = _buckets; + IEnumerable>> buckets = _buckets; if (where is not null) { buckets = buckets.Where(kvp => where(kvp.Key)); @@ -118,9 +118,9 @@ public IDictionary GetBucketSnapshot(Predicate? public TValue[] GetBucketSnapshot(TGroupKey group) { if (group == null) throw new ArgumentNullException(nameof(group)); - return _buckets.TryGetValue(group, out SortedSet bucket) ? bucket.ToArray() : Array.Empty(); + return _buckets.TryGetValue(group, out EnhancedSortedSet bucket) ? bucket.ToArray() : Array.Empty(); } - + /// /// Gets number of items in requested group. /// @@ -128,7 +128,7 @@ public TValue[] GetBucketSnapshot(TGroupKey group) public int GetBucketCount(TGroupKey group) { if (group == null) throw new ArgumentNullException(nameof(group)); - return _buckets.TryGetValue(group, out SortedSet bucket) ? bucket.Count : 0; + return _buckets.TryGetValue(group, out EnhancedSortedSet bucket) ? bucket.Count : 0; } /// @@ -139,22 +139,22 @@ public bool TryTakeFirst(out TValue first) { return TryRemove(GetKey(GetFirsts().Min), out first); } - + /// /// Returns best element of each bucket in supplied comparer order. /// [MethodImpl(MethodImplOptions.Synchronized)] - public SortedSet GetFirsts() + public EnhancedSortedSet GetFirsts() { - SortedSet sortedValues = new(_sortedComparer); - foreach (KeyValuePair> bucket in _buckets) + EnhancedSortedSet sortedValues = new(_sortedComparer); + foreach (KeyValuePair> bucket in _buckets) { sortedValues.Add(bucket.Value.Min!); } return sortedValues; } - + /// /// Gets last element in supplied comparer order. /// @@ -173,7 +173,7 @@ public bool TryGetLast(out TValue last) /// Bucket for same sender transactions. /// If element was removed. False if element was not present in pool. [MethodImpl(MethodImplOptions.Synchronized)] - public bool TryRemove(TKey key, out TValue value, [MaybeNullWhen(false)] out ICollection? bucket) => + public bool TryRemove(TKey key, out TValue value, [MaybeNullWhen(false)] out ICollection? bucket) => TryRemove(key, false, out value, out bucket); private bool TryRemove(TKey key, bool evicted, out TValue value, out ICollection? bucket) @@ -183,7 +183,7 @@ private bool TryRemove(TKey key, bool evicted, out TValue value, out ICollection if (Remove(key, value)) { TGroupKey groupMapping = MapToGroup(value); - if (_buckets.TryGetValue(groupMapping, out SortedSet bucketSet)) + if (_buckets.TryGetValue(groupMapping, out EnhancedSortedSet bucketSet)) { bucket = bucketSet; TValue? last = bucketSet.Max; @@ -198,11 +198,11 @@ private bool TryRemove(TKey key, bool evicted, out TValue value, out ICollection { UpdateSortedValues(bucketSet, last); } - + return true; } } - + Removed?.Invoke(this, new SortedPoolRemovedEventArgs(key, value, groupMapping, evicted)); } } @@ -216,7 +216,7 @@ private bool TryRemove(TKey key, bool evicted, out TValue value, out ICollection [MethodImpl(MethodImplOptions.Synchronized)] public bool TryRemove(TKey key, [MaybeNullWhen(false)] out TValue value) => TryRemove(key, out value, out _); - + [MethodImpl(MethodImplOptions.Synchronized)] public bool TryRemove(TKey key) => TryRemove(key, out _, out _); @@ -229,9 +229,9 @@ private bool TryRemove(TKey key, bool evicted, out TValue value, out ICollection [MethodImpl(MethodImplOptions.Synchronized)] public IEnumerable TakeWhile(TGroupKey groupKey, Predicate where) { - if (_buckets.TryGetValue(groupKey, out SortedSet? bucket)) + if (_buckets.TryGetValue(groupKey, out EnhancedSortedSet? bucket)) { - using SortedSet.Enumerator enumerator = bucket!.GetEnumerator(); + using EnhancedSortedSet.Enumerator enumerator = bucket!.GetEnumerator(); List? list = null; while (enumerator.MoveNext()) @@ -244,7 +244,7 @@ public IEnumerable TakeWhile(TGroupKey groupKey, Predicate where list ??= new List(); list.Add(enumerator.Current); } - + return list ?? Enumerable.Empty(); } return Enumerable.Empty(); @@ -298,7 +298,7 @@ public bool TryInsert(TKey key, TValue value, out TValue? removed) [MethodImpl(MethodImplOptions.Synchronized)] public bool TryInsert(TKey key, TValue value) => TryInsert(key, value, out _); - + private void RemoveLast(out TValue? removed) { TryRemove(_worstSortedValues.Max.Value, true, out removed, out _); @@ -316,15 +316,15 @@ protected virtual bool CanInsert(TKey key, TValue value) return !_cacheMap.ContainsKey(key); } - + /// /// Actual insert mechanism. /// protected virtual void InsertCore(TKey key, TValue value, TGroupKey groupKey) { - if (!_buckets.TryGetValue(groupKey, out SortedSet bucket)) + if (!_buckets.TryGetValue(groupKey, out EnhancedSortedSet bucket)) { - _buckets[groupKey] = bucket = new SortedSet(_groupComparer); + _buckets[groupKey] = bucket = new EnhancedSortedSet(_groupComparer); } TValue? last = bucket.Max; @@ -336,7 +336,7 @@ protected virtual void InsertCore(TKey key, TValue value, TGroupKey groupKey) } } - private void UpdateSortedValues(SortedSet bucket, TValue? previousLast) + private void UpdateSortedValues(EnhancedSortedSet bucket, TValue? previousLast) { TValue? newLast = bucket.Max; if (!Equals(previousLast, newLast)) @@ -351,19 +351,19 @@ private void UpdateSortedValues(SortedSet bucket, TValue? previousLast) } /// - /// Actual removal mechanism. + /// Actual removal mechanism. /// protected virtual bool Remove(TKey key, TValue value) => _cacheMap.Remove(key); [MethodImpl(MethodImplOptions.Synchronized)] public bool IsFull() => _cacheMap.Count >= _capacity; - + [MethodImpl(MethodImplOptions.Synchronized)] public bool TryGetBucket(TGroupKey groupKey, out TValue[] items) { - if (_buckets.TryGetValue(groupKey, out SortedSet bucket)) + if (_buckets.TryGetValue(groupKey, out EnhancedSortedSet bucket)) { items = bucket.ToArray(); return true; @@ -372,28 +372,28 @@ public bool TryGetBucket(TGroupKey groupKey, out TValue[] items) items = Array.Empty(); return false; } - + [MethodImpl(MethodImplOptions.Synchronized)] - public void UpdatePool(Func, IEnumerable<(TValue Tx, Action? Change)>> changingElements) + public void UpdatePool(Func, IEnumerable<(TValue Tx, Action? Change)>> changingElements) { - foreach ((TGroupKey groupKey, SortedSet bucket) in _buckets) + foreach ((TGroupKey groupKey, EnhancedSortedSet bucket) in _buckets) { changingElements(groupKey, bucket); UpdateGroup(groupKey, bucket, changingElements); } } - + [MethodImpl(MethodImplOptions.Synchronized)] - public void UpdateGroup(TGroupKey groupKey, Func, IEnumerable<(TValue Tx, Action? Change)>> changingElements) + public void UpdateGroup(TGroupKey groupKey, Func, IEnumerable<(TValue Tx, Action? Change)>> changingElements) { if (groupKey == null) throw new ArgumentNullException(nameof(groupKey)); - if (_buckets.TryGetValue(groupKey, out SortedSet bucket)) + if (_buckets.TryGetValue(groupKey, out EnhancedSortedSet bucket)) { UpdateGroup(groupKey, bucket, changingElements); } } - protected virtual void UpdateGroup(TGroupKey groupKey, SortedSet bucket, Func, IEnumerable<(TValue Tx, Action? Change)>> changingElements) + protected virtual void UpdateGroup(TGroupKey groupKey, EnhancedSortedSet bucket, Func, IEnumerable<(TValue Tx, Action? Change)>> changingElements) { foreach ((TValue value, Action? change) in changingElements(groupKey, bucket)) { diff --git a/src/Nethermind/Nethermind.TxPool/Collections/TxDistinctSortedPool.cs b/src/Nethermind/Nethermind.TxPool/Collections/TxDistinctSortedPool.cs index ba745f04d78..1051333e1d3 100644 --- a/src/Nethermind/Nethermind.TxPool/Collections/TxDistinctSortedPool.cs +++ b/src/Nethermind/Nethermind.TxPool/Collections/TxDistinctSortedPool.cs @@ -1,24 +1,25 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -// +// using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using Nethermind.Core; +using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Resettables; using Nethermind.Logging; @@ -29,8 +30,8 @@ namespace Nethermind.TxPool.Collections public class TxDistinctSortedPool : DistinctValueSortedPool { private readonly List _transactionsToRemove = new(); - - public TxDistinctSortedPool(int capacity, IComparer comparer, ILogManager logManager) + + public TxDistinctSortedPool(int capacity, IComparer comparer, ILogManager logManager) : base(capacity, comparer, CompetingTransactionEqualityComparer.Instance, logManager) { } @@ -38,15 +39,15 @@ public TxDistinctSortedPool(int capacity, IComparer comparer, ILogM protected override IComparer GetUniqueComparer(IComparer comparer) => comparer.GetPoolUniqueTxComparer(); protected override IComparer GetGroupComparer(IComparer comparer) => comparer.GetPoolUniqueTxComparerByNonce(); protected override IComparer GetReplacementComparer(IComparer comparer) => comparer.GetReplacementComparer(); - + protected override Address? MapToGroup(Transaction value) => value.MapTxToGroup(); protected override Keccak GetKey(Transaction value) => value.Hash!; - - protected override void UpdateGroup(Address groupKey, SortedSet bucket, Func, IEnumerable<(Transaction Tx, Action? Change)>> changingElements) + + protected override void UpdateGroup(Address groupKey, EnhancedSortedSet bucket, Func, IEnumerable<(Transaction Tx, Action? Change)>> changingElements) { _transactionsToRemove.Clear(); Transaction? lastElement = bucket.Max; - + foreach ((Transaction tx, Action? change) in changingElements(groupKey, bucket)) { if (change is null) diff --git a/src/Nethermind/Nethermind.TxPool/ITxPool.cs b/src/Nethermind/Nethermind.TxPool/ITxPool.cs index d40ec4a58ed..dbe76e51c8e 100644 --- a/src/Nethermind/Nethermind.TxPool/ITxPool.cs +++ b/src/Nethermind/Nethermind.TxPool/ITxPool.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -22,7 +22,7 @@ namespace Nethermind.TxPool { - public interface ITxPool + public interface ITxPool { int GetPendingTransactionsCount(); Transaction[] GetPendingTransactions(); @@ -39,6 +39,7 @@ public interface ITxPool bool IsKnown(Keccak? hash); bool TryGetPendingTransaction(Keccak hash, out Transaction? transaction); UInt256 ReserveOwnTransactionNonce(Address address); // TODO: this should be moved to a signer component, outside of TX pool + UInt256 GetLatestPendingNonce(Address address); event EventHandler NewDiscovered; event EventHandler NewPending; event EventHandler RemovedPending; diff --git a/src/Nethermind/Nethermind.TxPool/NullTxPool.cs b/src/Nethermind/Nethermind.TxPool/NullTxPool.cs index 4ab62e54213..723fb409fed 100644 --- a/src/Nethermind/Nethermind.TxPool/NullTxPool.cs +++ b/src/Nethermind/Nethermind.TxPool/NullTxPool.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -31,19 +31,19 @@ private NullTxPool() { } public int GetPendingTransactionsCount() => 0; public Transaction[] GetPendingTransactions() => Array.Empty(); - + public Transaction[] GetOwnPendingTransactions() => Array.Empty(); - + public IDictionary GetPendingTransactionsBySender() => new Dictionary(); public void AddPeer(ITxPoolPeer peer) { } public void RemovePeer(PublicKey nodeId) { } - + public AcceptTxResult SubmitTx(Transaction tx, TxHandlingOptions txHandlingOptions) => AcceptTxResult.Accepted; public bool RemoveTransaction(Keccak? hash) => false; - + public bool IsKnown(Keccak hash) => false; public bool TryGetPendingTransaction(Keccak hash, out Transaction? transaction) @@ -53,6 +53,7 @@ public bool TryGetPendingTransaction(Keccak hash, out Transaction? transaction) } public UInt256 ReserveOwnTransactionNonce(Address address) => UInt256.Zero; + public UInt256 GetLatestPendingNonce(Address address) => 0; public event EventHandler NewDiscovered { diff --git a/src/Nethermind/Nethermind.TxPool/TxPool.AddressNonces.cs b/src/Nethermind/Nethermind.TxPool/TxPool.AddressNonces.cs index 38148638f2a..0e7c1ca887a 100644 --- a/src/Nethermind/Nethermind.TxPool/TxPool.AddressNonces.cs +++ b/src/Nethermind/Nethermind.TxPool/TxPool.AddressNonces.cs @@ -1,21 +1,22 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -// +// using System.Collections.Concurrent; +using System.Linq; using System.Threading; using Nethermind.Int256; @@ -59,7 +60,7 @@ public NonceInfo ReserveNonce() { Interlocked.Exchange(ref _currentNonceInfo, newNonce); } - + return newNonce; } } diff --git a/src/Nethermind/Nethermind.TxPool/TxPool.cs b/src/Nethermind/Nethermind.TxPool/TxPool.cs index f18c98a6a39..d2b0fc7877a 100644 --- a/src/Nethermind/Nethermind.TxPool/TxPool.cs +++ b/src/Nethermind/Nethermind.TxPool/TxPool.cs @@ -330,7 +330,7 @@ private AcceptTxResult AddCore(Transaction tx, bool isPersistentBroadcast) } private IEnumerable<(Transaction Tx, Action? Change)> UpdateBucketWithAddedTransaction( - Address address, ICollection transactions) + Address address, IReadOnlyCollection transactions) { if (transactions.Count != 0) { @@ -346,7 +346,7 @@ private AcceptTxResult AddCore(Transaction tx, bool isPersistentBroadcast) } private IEnumerable<(Transaction Tx, Action? Change)> UpdateGasBottleneck( - ICollection transactions, long currentNonce, UInt256 balance) + IReadOnlyCollection transactions, long currentNonce, UInt256 balance) { UInt256? previousTxBottleneck = null; int i = 0; @@ -403,7 +403,7 @@ private void UpdateBuckets() } } - private IEnumerable<(Transaction Tx, Action? Change)> UpdateBucket(Address address, ICollection transactions) + private IEnumerable<(Transaction Tx, Action? Change)> UpdateBucket(Address address, IReadOnlyCollection transactions) { if (transactions.Count != 0) { @@ -519,6 +519,47 @@ public UInt256 ReserveOwnTransactionNonce(Address address) return currentNonce; } + public UInt256 GetLatestPendingNonce(Address address) + { + UInt256 maxPendingNonce = _accounts.GetAccount(address).Nonce; + + // we are not doing any updating, but lets just use a thread-safe method without any data copying like snapshot + _transactions.UpdateGroup(address, (_, transactions) => + { + // This is under the assumption that the addressTransactions are sorted by Nonce. + if (transactions.Count > 0) + { + // if we don't have any gaps we can easily calculate the nonce + Transaction lastTransaction = transactions.Max!; + if (maxPendingNonce + (UInt256)transactions.Count - 1 == lastTransaction.Nonce) + { + maxPendingNonce = lastTransaction.Nonce + 1; + } + + // we have a gap, need to scan the transactions + else + { + foreach (Transaction transaction in transactions) + { + if (transaction.Nonce == maxPendingNonce) + { + maxPendingNonce++; + } + else + { + break; + } + } + } + } + + // we won't do any actual changes + return Array.Empty<(Transaction Tx, Action? Change)>(); + }); + + return maxPendingNonce; + } + public bool IsKnown(Keccak hash) => _hashCache.Get(hash); public event EventHandler? NewDiscovered;