diff --git a/src/Nethermind/Nethermind.Api/INethermindApi.cs b/src/Nethermind/Nethermind.Api/INethermindApi.cs index fff51021393..7c600b9f056 100644 --- a/src/Nethermind/Nethermind.Api/INethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/INethermindApi.cs @@ -5,6 +5,8 @@ using System; using Nethermind.Config; using Nethermind.Core; +using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Serialization.Rlp; using Nethermind.Serialization.Rlp.TxDecoders; using Nethermind.TxPool; @@ -23,12 +25,14 @@ public T Config() where T : IConfig public static class NethermindApiExtensions { - public static void RegisterTxType(this INethermindApi api, TxType type, ITxDecoder decoder, ITxValidator validator) + public static void RegisterTxType(this INethermindApi api, ITxDecoder decoder, ITxValidator validator) where T : TransactionForRpc, IFromTransaction { ArgumentNullException.ThrowIfNull(api.TxValidator); + if (decoder.Type != T.TxType) throw new ArgumentException($"TxType mismatch decoder: {decoder.Type}, RPC: {T.TxType}"); - api.TxValidator.RegisterValidator(type, validator); + api.TxValidator.RegisterValidator(T.TxType, validator); TxDecoder.Instance.RegisterDecoder(decoder); + TransactionForRpc.RegisterTransactionType(); } } } diff --git a/src/Nethermind/Nethermind.Cli.Test/ProofCliModuleTests.cs b/src/Nethermind/Nethermind.Cli.Test/ProofCliModuleTests.cs index 3eac4d9207c..8f2f30409a8 100644 --- a/src/Nethermind/Nethermind.Cli.Test/ProofCliModuleTests.cs +++ b/src/Nethermind/Nethermind.Cli.Test/ProofCliModuleTests.cs @@ -7,10 +7,9 @@ using Nethermind.Cli.Modules; using Nethermind.Core.Crypto; using Nethermind.Core.Test.Builders; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc; using Nethermind.JsonRpc.Client; -using Nethermind.JsonRpc.Data; using Nethermind.Logging; using Nethermind.Serialization.Json; using NSubstitute; @@ -81,7 +80,7 @@ public void Get_transaction_receipt(bool includeHeader) public void Call() { Hash256 blockHash = TestItem.KeccakA; - TransactionForRpc tx = new() + LegacyTransactionForRpc tx = new() { From = TestItem.AddressA, To = TestItem.AddressB diff --git a/src/Nethermind/Nethermind.Cli/Modules/EthCliModule.cs b/src/Nethermind/Nethermind.Cli/Modules/EthCliModule.cs index 961ce37bfe9..94640022b6d 100644 --- a/src/Nethermind/Nethermind.Cli/Modules/EthCliModule.cs +++ b/src/Nethermind/Nethermind.Cli/Modules/EthCliModule.cs @@ -7,9 +7,8 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; -using Nethermind.JsonRpc.Data; namespace Nethermind.Cli.Modules { @@ -20,13 +19,15 @@ public class EthCliModule : CliModuleBase { long blockNumber = NodeManager.Post("eth_blockNumber").Result; - TransactionForRpc tx = new(); - tx.Value = amountInWei; - tx.Gas = Transaction.BaseTxGasCost; - tx.GasPrice = (UInt256)Engine.JintEngine.GetValue("gasPrice").AsNumber(); - tx.To = address; - tx.Nonce = (ulong)NodeManager.Post("eth_getTransactionCount", from, blockNumber).Result; - tx.From = from; + LegacyTransactionForRpc tx = new() + { + Value = amountInWei, + Gas = Transaction.BaseTxGasCost, + GasPrice = (UInt256)Engine.JintEngine.GetValue("gasPrice").AsNumber(), + To = address, + Nonce = (ulong)NodeManager.Post("eth_getTransactionCount", from, blockNumber).Result, + From = from + }; Hash256? keccak = NodeManager.Post("eth_sendTransaction", tx).Result; return keccak?.Bytes.ToHexString(); diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs index ac8cf8e0313..65cd94de826 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs @@ -39,6 +39,13 @@ static TestItem() Keccaks[i - 1] = Keccak.Compute(PublicKeys[i - 1].Bytes); ValueKeccaks[i - 1] = Keccaks[i - 1]; } + + byte[] r = new byte[32]; + byte[] s = new byte[32]; + r[1] = 1; + s[2] = 2; + RandomSignatureA = new Signature(r, s, 27); + RandomSignatureB = new Signature(r, s, 28); } public static Hash256 KeccakFromNumber(int i) @@ -93,6 +100,9 @@ public static Hash256 KeccakFromNumber(int i) public static Address AddressE = PublicKeyE.Address; public static Address AddressF = PublicKeyF.Address; + public static readonly Signature RandomSignatureA; + public static readonly Signature RandomSignatureB; + public static Withdrawal WithdrawalA_1Eth = new() { Address = AddressA, Index = 1, ValidatorIndex = 2001, AmountInGwei = 1_000_000_000 }; public static Withdrawal WithdrawalB_2Eth = new() { Address = AddressB, Index = 2, ValidatorIndex = 2002, AmountInGwei = 2_000_000_000 }; public static Withdrawal WithdrawalC_3Eth = new() { Address = AddressC, Index = 3, ValidatorIndex = 2003, AmountInGwei = 3_000_000_000 }; diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs index cf81f10a348..2246e7c319d 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs @@ -63,7 +63,7 @@ public TransactionBuilder WithCode(byte[] data) return this; } - public TransactionBuilder WithChainId(ulong chainId) + public TransactionBuilder WithChainId(ulong? chainId) { TestObjectInternal.ChainId = chainId; return this; @@ -298,6 +298,12 @@ public TransactionBuilder WithIsServiceTransaction(bool isServiceTransaction) return this; } + public TransactionBuilder WithSourceHash(Hash256? sourceHash) + { + TestObjectInternal.SourceHash = sourceHash; + return this; + } + public TransactionBuilder From(T item) { TestObjectInternal = item; diff --git a/src/Nethermind/Nethermind.Core/Extensions/UInt256Extensions.cs b/src/Nethermind/Nethermind.Core/Extensions/UInt256Extensions.cs new file mode 100644 index 00000000000..b0f06b73cd1 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Extensions/UInt256Extensions.cs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Int256; + +namespace Nethermind.Core.Extensions; + +public static class UInt256Extensions +{ + // value?.IsZero == false <=> x > 0 + public static bool IsPositive(this UInt256? @this) => @this?.IsZero == false; +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/AccessListItemForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/AccessListItemForRpc.cs deleted file mode 100644 index 0269aeeb0e6..00000000000 --- a/src/Nethermind/Nethermind.Facade/Eth/AccessListItemForRpc.cs +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using System.Linq; -using Nethermind.Core; -using Nethermind.Core.Collections; -using Nethermind.Core.Eip2930; -using Nethermind.Int256; -using Nethermind.Serialization.Json; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Nethermind.Facade.Eth -{ - public struct AccessListItemForRpc : IEquatable - { - [JsonConstructor] - public AccessListItemForRpc() { } - - public AccessListItemForRpc(Address address, IEnumerable? storageKeys) - { - Address = address; - StorageKeys = storageKeys; - } - - public Address Address { get; set; } - [JsonConverter(typeof(StorageCellIndexConverter))] - public IEnumerable? StorageKeys { get; set; } - - public static IEnumerable FromAccessList(AccessList accessList) => - accessList.Select(tuple => new AccessListItemForRpc(tuple.Address, tuple.StorageKeys)); - - public static AccessList ToAccessList(IEnumerable accessList) - { - AccessList.Builder builder = new(); - foreach (AccessListItemForRpc accessListItem in accessList) - { - builder.AddAddress(accessListItem.Address); - if (accessListItem.StorageKeys is not null) - { - foreach (UInt256 index in accessListItem.StorageKeys) - { - builder.AddStorage(index); - } - } - } - - return builder.Build(); - } - - public readonly bool Equals(AccessListItemForRpc other) => Equals(Address, other.Address) && StorageKeys.NullableSequenceEqual(other.StorageKeys); - public override readonly bool Equals(object? obj) => obj is AccessListItemForRpc other && Equals(other); - public override readonly int GetHashCode() => HashCode.Combine(Address, StorageKeys); - } -} diff --git a/src/Nethermind/Nethermind.Facade/Eth/AuthorizationTupleForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/AuthorizationTupleForRpc.cs deleted file mode 100644 index c35c97b1d56..00000000000 --- a/src/Nethermind/Nethermind.Facade/Eth/AuthorizationTupleForRpc.cs +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core; -using Nethermind.Int256; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; - -namespace Nethermind.Facade.Eth -{ - public struct AuthorizationTupleForRpc - { - [JsonConstructor] - public AuthorizationTupleForRpc() - { - } - public AuthorizationTupleForRpc(UInt256 chainId, ulong nonce, Address address, UInt256? yParity, UInt256? s, UInt256? r) - { - ChainId = chainId; - Nonce = nonce; - Address = address; - YParity = yParity; - S = s; - R = r; - } - - public UInt256 ChainId { get; set; } - public ulong Nonce { get; set; } - public Address Address { get; set; } - public UInt256? YParity { get; set; } - public UInt256? S { get; set; } - public UInt256? R { get; set; } - - public static IEnumerable FromAuthorizationList(AuthorizationTuple[] authorizationList) => - authorizationList.Select(tuple => new AuthorizationTupleForRpc(tuple.ChainId, - tuple.Nonce, - tuple.CodeAddress, - tuple.AuthoritySignature.RecoveryId, - new UInt256(tuple.AuthoritySignature.S), - new UInt256(tuple.AuthoritySignature.R))); - } -} diff --git a/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs index b0904d5552f..c194e214615 100644 --- a/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs +++ b/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs @@ -12,6 +12,7 @@ using Nethermind.Serialization.Rlp; using System.Text.Json.Serialization; using System.Runtime.CompilerServices; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Core.ConsensusRequests; namespace Nethermind.Facade.Eth; @@ -78,7 +79,10 @@ public BlockForRpc(Block block, bool includeFullTransactionData, ISpecProvider s StateRoot = block.StateRoot; Timestamp = block.Timestamp; TotalDifficulty = block.TotalDifficulty ?? 0; - Transactions = includeFullTransactionData ? block.Transactions.Select((t, idx) => new TransactionForRpc(block.Hash, block.Number, idx, t, block.BaseFeePerGas)).ToArray() : block.Transactions.Select(t => t.Hash).OfType().ToArray(); + Transactions = (includeFullTransactionData + ? block.Transactions.Select((t, idx) => TransactionForRpc.FromTransaction(t, block.Hash, block.Number, idx, block.BaseFeePerGas, specProvider.ChainId)) + : block.Transactions.Select(t => t.Hash).OfType()) + .ToArray(); TransactionsRoot = block.TxRoot; Uncles = block.Uncles.Select(o => o.Hash); Withdrawals = block.Withdrawals; diff --git a/src/Nethermind/Nethermind.Facade/Eth/IFromTransaction.cs b/src/Nethermind/Nethermind.Facade/Eth/IFromTransaction.cs new file mode 100644 index 00000000000..cebf8c7b8d2 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/IFromTransaction.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth; + +public interface IFromTransaction : ITxTyped where T : TransactionForRpc +{ + static abstract T FromTransaction(Transaction tx, TransactionConverterExtraData extraData); +} + +public readonly struct TransactionConverterExtraData +{ + public ulong? ChainId { get; init; } + public Hash256? BlockHash { get; init; } + public long? BlockNumber { get; init; } + public int? TxIndex { get; init; } + public UInt256? BaseFee { get; init; } + public TxReceipt Receipt { get; init; } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/ITxTyped.cs b/src/Nethermind/Nethermind.Facade/Eth/ITxTyped.cs new file mode 100644 index 00000000000..72674318cca --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/ITxTyped.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; + +namespace Nethermind.Facade.Eth; + +public interface ITxTyped +{ + static abstract TxType TxType { get; } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs new file mode 100644 index 00000000000..05e3a368451 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListForRpc.cs @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Int256; +using Nethermind.Serialization.Json; +using System.Text.Json.Serialization; +using System.Text.Json; +using System; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +[JsonConverter(typeof(JsonConverter))] +public class AccessListForRpc +{ + private readonly IEnumerable _items; + + [JsonConstructor] + public AccessListForRpc() { } + + private AccessListForRpc(IEnumerable items) + { + _items = items; + } + + private class Item + { + public Address Address { get; set; } + + [JsonConverter(typeof(StorageCellIndexConverter))] + public IEnumerable? StorageKeys { get; set; } + + [JsonConstructor] + public Item() { } + + public Item(Address address, IEnumerable storageKeys) + { + Address = address; + StorageKeys = storageKeys; + } + } + + public static AccessListForRpc FromAccessList(AccessList? accessList) => + accessList is null + ? new AccessListForRpc([]) + : new AccessListForRpc(accessList.Select(item => new Item(item.Address, [.. item.StorageKeys]))); + + public AccessList ToAccessList() + { + AccessList.Builder builder = new(); + foreach (Item item in _items) + { + builder.AddAddress(item.Address); + foreach (UInt256 index in item.StorageKeys ?? []) + { + builder.AddStorage(index); + } + } + + return builder.Build(); + } + + public class JsonConverter : JsonConverter + { + public override AccessListForRpc? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + List? list = JsonSerializer.Deserialize>(ref reader, options); + return list is null ? null : new AccessListForRpc(list); + } + + public override void Write(Utf8JsonWriter writer, AccessListForRpc value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value._items, options); + } + } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListTransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListTransactionForRpc.cs new file mode 100644 index 00000000000..586769ba437 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AccessListTransactionForRpc.cs @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class AccessListTransactionForRpc : LegacyTransactionForRpc, IFromTransaction +{ + public new static TxType TxType => TxType.AccessList; + + public override TxType? Type => TxType; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public AccessListForRpc? AccessList { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? YParity { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public sealed override ulong? ChainId { get; set; } + + [JsonConstructor] + public AccessListTransactionForRpc() { } + + public AccessListTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, ulong? chainId = null) + : base(transaction, txIndex, blockHash, blockNumber) + { + AccessList = AccessListForRpc.FromAccessList(transaction.AccessList); + YParity = transaction.Signature?.RecoveryId ?? 0; + ChainId = transaction.ChainId ?? chainId ?? BlockchainIds.Mainnet; + V = YParity ?? 0; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.AccessList = AccessList?.ToAccessList() ?? Core.Eip2930.AccessList.Empty; + + return tx; + } + + public new static AccessListTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, chainId: extraData.ChainId); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs new file mode 100644 index 00000000000..d0940697458 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/AuthorizationListForRpc.cs @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; +using Nethermind.Int256; +using System.Text.Json.Serialization; +using System.Text.Json; +using System; +using Nethermind.Core.Crypto; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +[JsonConverter(typeof(JsonConverter))] +public class AuthorizationListForRpc +{ + private readonly IEnumerable _tuples; + + [JsonConstructor] + public AuthorizationListForRpc() { } + + private AuthorizationListForRpc(IEnumerable tuples) + { + _tuples = tuples; + } + + private class Tuple + { + public UInt256 ChainId { get; set; } + public ulong Nonce { get; set; } + public Address Address { get; set; } + + // TODO: Are these fields actually nullable + public UInt256? YParity { get; set; } + public UInt256? S { get; set; } + public UInt256? R { get; set; } + + [JsonConstructor] + public Tuple() { } + + public Tuple(UInt256 chainId, ulong nonce, Address address, UInt256? yParity, UInt256? s, UInt256? r) + { + ChainId = chainId; + Nonce = nonce; + Address = address; + YParity = yParity; + S = s; + R = r; + } + } + + public static AuthorizationListForRpc FromAuthorizationList(IEnumerable? authorizationList) => + authorizationList is null + ? new AuthorizationListForRpc([]) + : new AuthorizationListForRpc(authorizationList.Select(tuple => + new Tuple(tuple.ChainId, + tuple.Nonce, + tuple.CodeAddress, + tuple.AuthoritySignature.RecoveryId, + new UInt256(tuple.AuthoritySignature.S), + new UInt256(tuple.AuthoritySignature.R)))); + + public AuthorizationTuple[] ToAuthorizationList() => _tuples + .Select(tuple => new AuthorizationTuple( + (ulong)tuple.ChainId, + tuple.Address, + tuple.Nonce, + // TODO: This might be wrong + new Signature(tuple.R ?? 0, tuple.S ?? 0, (ulong)(tuple.YParity ?? 0))) + ).ToArray(); + + public class JsonConverter : JsonConverter + { + public override AuthorizationListForRpc? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + List? list = JsonSerializer.Deserialize>(ref reader, options); + return list is null ? null : new AuthorizationListForRpc(list); + } + + public override void Write(Utf8JsonWriter writer, AuthorizationListForRpc value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value._tuples, options); + } + } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/BlobTransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/BlobTransactionForRpc.cs new file mode 100644 index 00000000000..4b7cd41f095 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/BlobTransactionForRpc.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class BlobTransactionForRpc : EIP1559TransactionForRpc, IFromTransaction +{ + public new static TxType TxType => TxType.Blob; + + public override TxType? Type => TxType; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? MaxFeePerBlobGas { get; set; } + + // TODO: Each item should be a 32 byte array + // Currently we don't enforce this (hashes can have any length) + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public byte[][]? BlobVersionedHashes { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public byte[]? Blobs { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? GasPrice { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? V { get; set; } + + [JsonConstructor] + public BlobTransactionForRpc() { } + + public BlobTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, UInt256? baseFee = null, ulong? chainId = null) + : base(transaction, txIndex, blockHash, blockNumber, baseFee, chainId) + { + MaxFeePerBlobGas = transaction.MaxFeePerBlobGas ?? 0; + BlobVersionedHashes = transaction.BlobVersionedHashes ?? []; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.MaxFeePerBlobGas = MaxFeePerBlobGas; + tx.BlobVersionedHashes = BlobVersionedHashes; + + return tx; + } + + public new static BlobTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, baseFee: extraData.BaseFee, chainId: extraData.ChainId); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/EIP1559TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/EIP1559TransactionForRpc.cs new file mode 100644 index 00000000000..fe5c98f4625 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/EIP1559TransactionForRpc.cs @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Extensions; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class EIP1559TransactionForRpc : AccessListTransactionForRpc, IFromTransaction +{ + public new static TxType TxType => TxType.EIP1559; + + public override TxType? Type => TxType.EIP1559; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? MaxPriorityFeePerGas { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? MaxFeePerGas { get; set; } + + [JsonConstructor] + public EIP1559TransactionForRpc() { } + + public EIP1559TransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, UInt256? baseFee = null, ulong? chainId = null) + : base(transaction, txIndex, blockHash, blockNumber, chainId) + { + MaxFeePerGas = transaction.MaxFeePerGas; + MaxPriorityFeePerGas = transaction.MaxPriorityFeePerGas; + GasPrice = baseFee is not null + ? transaction.CalculateEffectiveGasPrice(eip1559Enabled: true, baseFee.Value) + : transaction.MaxFeePerGas; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.GasPrice = MaxPriorityFeePerGas ?? 0; + tx.DecodedMaxFeePerGas = MaxFeePerGas ?? 0; + + return tx; + } + + public override bool ShouldSetBaseFee() => + base.ShouldSetBaseFee() || MaxFeePerGas.IsPositive() || MaxPriorityFeePerGas.IsPositive(); + + public new static EIP1559TransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, baseFee: extraData.BaseFee, chainId: extraData.ChainId); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/LegacyTransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/LegacyTransactionForRpc.cs new file mode 100644 index 00000000000..3b77fc2dc88 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/LegacyTransactionForRpc.cs @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class LegacyTransactionForRpc : TransactionForRpc, ITxTyped, IFromTransaction +{ + public static TxType TxType => TxType.Legacy; + + public override TxType? Type => TxType; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? Nonce { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Address? To { get; set; } + + // NOTE: This field exists only during deserialization according to the Ethereum JSON-RPC spec. + // No transaction types include a `From` field when serializing. + // For backwards compatibility with previous Nethermind versions we also serialize it. + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Address? From { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public long? Gas { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public UInt256? Value { get; set; } + + // Required for compatibility with some CLs like Prysm + // Accept during deserialization, ignore during serialization + // See: https://github.com/NethermindEth/nethermind/pull/6067 + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public byte[]? Data { set { Input = value; } private get { return null; } } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public byte[]? Input { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? GasPrice { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public virtual ulong? ChainId { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? V { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? R { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual UInt256? S { get; set; } + + [JsonConstructor] + public LegacyTransactionForRpc() { } + + public LegacyTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null) + : base(transaction, txIndex, blockHash, blockNumber) + { + Nonce = transaction.Nonce; + To = transaction.To; + From = transaction.SenderAddress; + Gas = transaction.GasLimit; + Value = transaction.Value; + Input = transaction.Data.AsArray() ?? []; + GasPrice = transaction.GasPrice; + ChainId = transaction.ChainId; + + R = new UInt256(transaction.Signature?.R ?? [], true); + S = new UInt256(transaction.Signature?.S ?? [], true); + V = transaction.Signature?.V ?? 0; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.Nonce = Nonce ?? 0; // TODO: Should we pick the last nonce? + tx.To = To; + tx.GasLimit = Gas ?? 90_000; + tx.Value = Value ?? 0; + tx.Data = Input; + tx.GasPrice = GasPrice ?? 20.GWei(); + tx.ChainId = ChainId; + tx.SenderAddress = From ?? Address.SystemUser; + + return tx; + } + + // TODO: Can we remove this code? + public override void EnsureDefaults(long? gasCap) + { + if (gasCap is null || gasCap == 0) + gasCap = long.MaxValue; + + Gas = Gas is null || Gas == 0 + ? gasCap + : Math.Min(gasCap.Value, Gas.Value); + + From ??= Address.SystemUser; + } + + public override bool ShouldSetBaseFee() => GasPrice.IsPositive(); + + public static LegacyTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) => + new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/SetCodeTransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/SetCodeTransactionForRpc.cs new file mode 100644 index 00000000000..27c84113d2e --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/SetCodeTransactionForRpc.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +public class SetCodeTransactionForRpc : EIP1559TransactionForRpc, IFromTransaction +{ + public new static TxType TxType => TxType.SetCode; + + public override TxType? Type => TxType; + + public AuthorizationListForRpc AuthorizationList { get; set; } + + #region Deprecated fields + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? GasPrice { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? YParity { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? V { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? R { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public override UInt256? S { get; set; } + #endregion + + [JsonConstructor] + public SetCodeTransactionForRpc() { } + + public SetCodeTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, UInt256? baseFee = null, ulong? chainId = null) + : base(transaction, txIndex, blockHash, blockNumber, baseFee, chainId) + { + AuthorizationList = AuthorizationListForRpc.FromAuthorizationList(transaction.AuthorizationList); + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.AuthorizationList = AuthorizationList?.ToAuthorizationList() ?? []; + + return tx; + } + + public new static SetCodeTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, baseFee: extraData.BaseFee, chainId: extraData.ChainId); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/TransactionForRpc.cs new file mode 100644 index 00000000000..9a614aca1b4 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/RpcTransaction/TransactionForRpc.cs @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; + +namespace Nethermind.Facade.Eth.RpcTransaction; + +/// +/// Base class for all output Nethermind RPC Transactions. +/// +/// +/// Input: +/// JSON -> (through , a registry of [ => subtypes) +/// -> (through an overloaded method) +/// Output: +/// -> (through , a registry of [ => ) +/// -> JSON (Derived by System.Text.JSON using the runtime type) +/// +[JsonConverter(typeof(JsonConverter))] +public abstract class TransactionForRpc +{ + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public virtual TxType? Type => null; + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Hash256? Hash { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public long? TransactionIndex { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Hash256? BlockHash { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public long? BlockNumber { get; set; } + + [JsonConstructor] + protected TransactionForRpc() { } + + protected TransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null) + { + Hash = transaction.Hash; + TransactionIndex = txIndex; + BlockHash = blockHash; + BlockNumber = blockNumber; + } + + public virtual Transaction ToTransaction() + { + return new Transaction + { + Type = Type ?? default, + }; + } + + public abstract void EnsureDefaults(long? gasCap); + public abstract bool ShouldSetBaseFee(); + + internal class JsonConverter : JsonConverter + { + // NOTE: Should we default to a specific TxType? + private const TxType DefaultTxType = TxType.Legacy; + private static readonly Type[] _types = new Type[Transaction.MaxTxType + 1]; + + public JsonConverter() + { + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + } + + internal static void RegisterTransactionType() where T : TransactionForRpc, ITxTyped + => _types[(int)T.TxType] = typeof(T); + + public override TransactionForRpc? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Copy the reader so we can do a double parse: + // The first parse extract the `Type` while the second parses the entire Transaction + Utf8JsonReader txTypeReader = reader; + TransactionType type = JsonSerializer.Deserialize(ref txTypeReader, options); + + TxType discriminator = (TxType)(type.Type ?? (ulong)DefaultTxType); + + return _types.TryGetByTxType(discriminator, out Type concreteTxType) + ? (TransactionForRpc?)JsonSerializer.Deserialize(ref reader, concreteTxType, options) + : throw new JsonException("Unknown transaction type"); + } + + public override void Write(Utf8JsonWriter writer, TransactionForRpc value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } + } + + /// + /// Used only for finding the field when deserializing a + /// + private struct TransactionType + { + // Hex value + public ulong? Type { get; set; } + } + + internal class TransactionConverter + { + private delegate TransactionForRpc FromTransactionFunc(Transaction tx, TransactionConverterExtraData extraData); + + private static readonly FromTransactionFunc?[] _fromTransactionFuncs = new FromTransactionFunc?[Transaction.MaxTxType + 1]; + + static TransactionConverter() + { + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + RegisterTransactionType(); + } + + internal static void RegisterTransactionType() where T : TransactionForRpc, IFromTransaction + => _fromTransactionFuncs[(byte)T.TxType] = T.FromTransaction; + + public static TransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + { + return _fromTransactionFuncs.TryGetByTxType(tx.Type, out var FromTransaction) + ? FromTransaction(tx, extraData) + : throw new ArgumentException("No converter for transaction type"); + } + } + + public static void RegisterTransactionType() where T : TransactionForRpc, IFromTransaction + { + JsonConverter.RegisterTransactionType(); + TransactionConverter.RegisterTransactionType(); + } + + public static TransactionForRpc FromTransaction(Transaction transaction, Hash256? blockHash = null, long? blockNumber = null, int? txIndex = null, UInt256? baseFee = null, ulong? chainId = null) => + TransactionConverter.FromTransaction(transaction, new() + { + ChainId = chainId, + TxIndex = txIndex, + BlockHash = blockHash, + BlockNumber = blockNumber, + BaseFee = baseFee + }); +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs deleted file mode 100644 index 5b054f48106..00000000000 --- a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Collections.Generic; -using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Core.Eip2930; -using Nethermind.Core.Extensions; -using Nethermind.Int256; - -namespace Nethermind.Facade.Eth; - -public class TransactionForRpc -{ - // HACK: To ensure that serialized Txs always have a `ChainId` we keep the last loaded `ChainSpec`. - // See: https://github.com/NethermindEth/nethermind/pull/6061#discussion_r1321634914 - public static UInt256? DefaultChainId { get; set; } - - public TransactionForRpc(Transaction transaction) : this(null, null, null, transaction) { } - - public TransactionForRpc(Hash256? blockHash, long? blockNumber, int? txIndex, Transaction transaction, UInt256? baseFee = null) - { - Hash = transaction.Hash; - Nonce = transaction.Nonce; - BlockHash = blockHash; - BlockNumber = blockNumber; - TransactionIndex = txIndex; - From = transaction.SenderAddress; - To = transaction.To; - Value = transaction.Value; - GasPrice = transaction.GasPrice; - Gas = transaction.GasLimit; - Input = Data = transaction.Data.AsArray(); - if (transaction.Supports1559) - { - GasPrice = baseFee is not null - ? transaction.CalculateEffectiveGasPrice(true, baseFee.Value) - : transaction.MaxFeePerGas; - MaxFeePerGas = transaction.MaxFeePerGas; - MaxPriorityFeePerGas = transaction.MaxPriorityFeePerGas; - } - if (transaction.Type > TxType.Legacy) - { - ChainId = transaction.ChainId - ?? DefaultChainId - ?? BlockchainIds.Mainnet; - } - else - { - ChainId = transaction.ChainId; - } - Type = transaction.Type; - if (transaction.SupportsAccessList) - { - AccessList = transaction.AccessList is null ? Array.Empty() : AccessListItemForRpc.FromAccessList(transaction.AccessList); - } - else - { - AccessList = null; - } - AuthorizationList = transaction.SupportsAuthorizationList - ? transaction.AuthorizationList is null - ? Array.Empty() - : AuthorizationTupleForRpc.FromAuthorizationList(transaction.AuthorizationList) - : null; - MaxFeePerBlobGas = transaction.MaxFeePerBlobGas; - BlobVersionedHashes = transaction.BlobVersionedHashes; - - Signature? signature = transaction.Signature; - if (signature is not null) - { - - YParity = transaction.SupportsAccessList ? signature.RecoveryId : null; - R = new UInt256(signature.R, true); - S = new UInt256(signature.S, true); - // V must be null for non-legacy transactions. Temporarily set to recovery id for Geth compatibility. - // See https://github.com/ethereum/go-ethereum/issues/27727 - V = transaction.Type == TxType.Legacy ? signature.V : signature.RecoveryId; - } - } - - // ReSharper disable once UnusedMember.Global - [JsonConstructor] - public TransactionForRpc() { } - - public Hash256? SourceHash { get; set; } - public UInt256? Mint { get; set; } - public bool? IsSystemTx { get; set; } // this is the IsOpSystemTransaction flag - - public Hash256? Hash { get; set; } - public UInt256? Nonce { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public Hash256? BlockHash { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public long? BlockNumber { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public long? TransactionIndex { get; set; } - - public Address? From { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public Address? To { get; set; } - - public UInt256? Value { get; set; } - public UInt256? GasPrice { get; set; } - - public UInt256? MaxPriorityFeePerGas { get; set; } - - public UInt256? MaxFeePerGas { get; set; } - public long? Gas { get; set; } - - // Required for compatibility with some CLs like Prysm - // Accept during deserialization, ignore during serialization - // See: https://github.com/NethermindEth/nethermind/pull/6067 - [JsonPropertyName(nameof(Data))] - [JsonConverter(typeof(DataConverter))] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public byte[]? Data { set { Input = value; } private get { return null; } } - - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public byte[]? Input { get; set; } - - public UInt256? ChainId { get; set; } - - public TxType Type { get; set; } - - public IEnumerable? AccessList { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public IEnumerable? AuthorizationList { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public UInt256? MaxFeePerBlobGas { get; set; } // eip4844 - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public byte[][]? BlobVersionedHashes { get; set; } // eip4844 - - public UInt256? V { get; set; } - - public UInt256? S { get; set; } - - public UInt256? R { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public UInt256? YParity { get; set; } - - public Transaction ToTransactionWithDefaults(ulong? chainId = null) => ToTransactionWithDefaults(chainId); - - public T ToTransactionWithDefaults(ulong? chainId = null) where T : Transaction, new() - { - T tx = new() - { - GasLimit = Gas ?? 90000, - GasPrice = GasPrice ?? 20.GWei(), - Nonce = (ulong)(Nonce ?? 0), // here pick the last nonce? - To = To, - SenderAddress = From, - Value = Value ?? 0, - Data = Input, - Type = Type, - AccessList = TryGetAccessList(), - ChainId = chainId, - DecodedMaxFeePerGas = MaxFeePerGas ?? 0, - Hash = Hash - }; - - if (tx.Supports1559) - { - tx.GasPrice = MaxPriorityFeePerGas ?? 0; - } - - if (tx.SupportsBlobs) - { - tx.MaxFeePerBlobGas = MaxFeePerBlobGas; - tx.BlobVersionedHashes = BlobVersionedHashes; - } - - return tx; - } - - public Transaction ToTransaction(ulong? chainId = null) => ToTransaction(); - - public T ToTransaction(ulong? chainId = null) where T : Transaction, new() - { - byte[]? data = Input; - - T tx = new() - { - GasLimit = Gas ?? 0, - GasPrice = GasPrice ?? 0, - Nonce = (ulong)(Nonce ?? 0), // here pick the last nonce? - To = To, - SenderAddress = From, - Value = Value ?? 0, - Data = (Memory?)data, - Type = Type, - AccessList = TryGetAccessList(), - ChainId = chainId, - }; - - if (data is null) - { - tx.Data = null; // Yes this is needed... really. Try a debugger. - } - - if (tx.Supports1559) - { - tx.GasPrice = MaxPriorityFeePerGas ?? 0; - tx.DecodedMaxFeePerGas = MaxFeePerGas ?? 0; - } - - if (tx.SupportsBlobs) - { - tx.MaxFeePerBlobGas = MaxFeePerBlobGas; - tx.BlobVersionedHashes = BlobVersionedHashes; - } - - return tx; - } - - private AccessList? TryGetAccessList() => - !Type.IsTxTypeWithAccessList() || AccessList is null - ? null - : AccessListItemForRpc.ToAccessList(AccessList); - - public void EnsureDefaults(long? gasCap) - { - if (gasCap is null || gasCap == 0) - gasCap = long.MaxValue; - - Gas = Gas is null || Gas == 0 - ? gasCap - : Math.Min(gasCap.Value, Gas.Value); - - From ??= Address.SystemUser; - } - - private class DataConverter : JsonConverter - { - public override bool HandleNull { get; } = false; - - public override byte[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return JsonSerializer.Deserialize(ref reader, options); - } - - public override void Write(Utf8JsonWriter writer, byte[]? value, JsonSerializerOptions options) - { - throw new NotSupportedException(); - } - } -} diff --git a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs index db5341d33b4..bc77defb9d5 100644 --- a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs +++ b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs @@ -144,7 +144,7 @@ public virtual async Task Execute(CancellationToken cancellationToken) StepDependencyException.ThrowIfNull(_api.TxPoolInfoProvider); - TxPoolRpcModule txPoolRpcModule = new(_api.TxPoolInfoProvider, _api.LogManager); + TxPoolRpcModule txPoolRpcModule = new(_api.TxPoolInfoProvider, _api.SpecProvider); rpcModuleProvider.RegisterSingle(txPoolRpcModule); StepDependencyException.ThrowIfNull(_api.SyncServer); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Data/AccessListItemForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Data/AccessListItemForRpcTests.cs deleted file mode 100644 index ae948e1d693..00000000000 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Data/AccessListItemForRpcTests.cs +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; -using FluentAssertions; -using Nethermind.Core; -using Nethermind.Core.Eip2930; -using Nethermind.Core.Test.Builders; -using Nethermind.Facade.Eth; -using Nethermind.Int256; -using Nethermind.JsonRpc.Data; -using NUnit.Framework; - -namespace Nethermind.JsonRpc.Test.Data; - -public class AccessListItemForRpcTests -{ - [Test] - public void Single_address_with_no_storage() - { - Address address = TestItem.AddressA; - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - AccessListItemForRpc[] expected = { new(address, new UInt256[] { }) }; - forRpc.Should().BeEquivalentTo(expected); - } - - [Test] - public void Single_address_with_multiple_storage_keys() - { - Address address = TestItem.AddressA; - UInt256 storageKey1 = (UInt256)1; - UInt256 storageKey2 = (UInt256)2; - UInt256 storageKey3 = (UInt256)3; - - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey2) - .AddStorage(storageKey3) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - AccessListItemForRpc[] expected = { new(address, new[] { storageKey1, storageKey2, storageKey3 }) }; - forRpc.Should().BeEquivalentTo(expected); - } - - [Test] - public void Single_address_with_duplicated_storage_keys() - { - Address address = TestItem.AddressA; - UInt256 storageKey1 = (UInt256)1; - UInt256 storageKey2 = (UInt256)2; - UInt256 storageKey3 = (UInt256)3; - - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey2) - .AddStorage(storageKey3) - .AddStorage(storageKey1) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - - AccessListItemForRpc[] expected = { new(address, new[] { storageKey1, storageKey2, storageKey3, storageKey1 }) }; - forRpc.Should().BeEquivalentTo(expected); - } - - [Test] - public void Duplicated_address_with_multiple_storage_keys() - { - Address address = TestItem.AddressA; - UInt256 storageKey1 = (UInt256)1; - UInt256 storageKey2 = (UInt256)2; - UInt256 storageKey3 = (UInt256)3; - - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey2) - .AddAddress(address) - .AddStorage(storageKey3) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - - AccessListItemForRpc[] expected = { new(address, new[] { storageKey1, storageKey2 }), new(address, new[] { storageKey3 }) }; - forRpc.Should().BeEquivalentTo(expected); - } - - [Test] - public void Duplicated_address_with_duplicated_storage_keys() - { - Address address = TestItem.AddressA; - UInt256 storageKey1 = (UInt256)1; - UInt256 storageKey2 = (UInt256)2; - UInt256 storageKey3 = (UInt256)3; - - AccessList accessList = new AccessList.Builder() - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey2) - .AddAddress(address) - .AddStorage(storageKey1) - .AddStorage(storageKey3) - .Build(); - - IEnumerable forRpc = AccessListItemForRpc.FromAccessList(accessList); - AccessListItemForRpc[] expected = { new(address, new[] { storageKey1, storageKey2 }), new(address, new[] { storageKey1, storageKey3 }) }; - forRpc.Should().BeEquivalentTo(expected); - } -} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs index f34daa6963f..858fc63ff34 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs @@ -1,16 +1,14 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Collections.Generic; +using System; using FluentAssertions; using FluentAssertions.Json; using Nethermind.Core; using Nethermind.Core.Eip2930; using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; -using Nethermind.Facade.Eth; -using Nethermind.Int256; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Serialization.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; @@ -44,11 +42,12 @@ public void can_serialize_valid_accessList(TxType txType, string txJson) Type = txType, AccessList = GetTestAccessList(), }; - TransactionForRpc transactionForRpc = new(transaction); - + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(transactionForRpc); - JToken.Parse(serialized).Should().BeEquivalentTo(JToken.Parse(txJson)); + var actual = JObject.Parse(serialized).Property("accessList"); + var expected = JObject.Parse(txJson).Property("accessList"); + actual.Should().BeEquivalentTo(expected); } [TestCase(TxType.AccessList, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01","chainId":"0x01","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x1","0x2","0x3","0x5","0x8"]},{"address":"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358","storageKeys":["0x2a"]}]}""")] @@ -60,7 +59,7 @@ public void can_deserialize_valid_accessList(TxType txType, string txJson) Type = txType, AccessList = GetTestAccessList(), }; - TransactionForRpc transactionForRpc = new(transaction); + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(transaction); TransactionForRpc deserializedTxForRpc = _serializer.Deserialize(txJson); @@ -74,7 +73,7 @@ public void can_serialize_null_accessList_to_nothing(TxType txType) { Type = txType, }; - TransactionForRpc rpc = new(transaction); + var rpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(rpc); @@ -89,7 +88,7 @@ public void can_serialize_null_accessList_to_empty_array(TxType txType) { Type = txType, }; - TransactionForRpc rpc = new(transaction); + var rpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(rpc); @@ -101,10 +100,10 @@ public void can_deserialize_null_accessList() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01"}"""; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); + var transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.AccessList); - transactionForRpc.AccessList.Should().BeNull(); + ((AccessListTransactionForRpc)transactionForRpc).AccessList.Should().BeNull(); } [Test] @@ -113,92 +112,100 @@ public void can_deserialize_no_accessList() string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x0"}"""; TransactionForRpc transactionForRpc = _serializer.Deserialize(json); + var transaction = transactionForRpc.ToTransaction(); - transactionForRpc.Type.Should().Be(TxType.Legacy); - transactionForRpc.AccessList.Should().BeNull(); + transaction.Type.Should().Be(TxType.Legacy); + transaction.AccessList.Should().BeNull(); } [TestCase(TxType.AccessList, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x1","chainId":"0x1","accessList":[]}""")] [TestCase(TxType.EIP1559, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","maxPriorityFeePerGas":"0x0","maxFeePerGas":"0x0","gas":"0x0","input":null,"type":"0x2","chainId":"0x1","accessList":[]}""")] public void can_serialize_empty_accessList(TxType txType, string txJson) { - AccessList.Builder builder = new(); Transaction transaction = new() { Type = txType, - AccessList = builder.Build(), + AccessList = AccessList.Empty, }; - TransactionForRpc transactionForRpc = new(transaction); - + var transactionForRpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(transactionForRpc); - JToken.Parse(serialized).Should().BeEquivalentTo(JToken.Parse(txJson)); + var actual = JObject.Parse(serialized).Property("accessList"); + var expected = JObject.Parse(txJson).Property("accessList"); + actual.Should().BeEquivalentTo(expected); } [Test] public void can_deserialize_empty_accessList() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01","accessList":[]}"""; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.AccessList); - transactionForRpc.AccessList!.Should().BeEmpty(); + + var expected = AccessList.Empty; + var actual = ((AccessListTransactionForRpc)transactionForRpc).AccessList!.ToAccessList(); + actual.Should().BeEquivalentTo(expected); } [TestCase(TxType.AccessList, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x1","chainId":"0x1","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":[]}]}""")] [TestCase(TxType.EIP1559, """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","maxPriorityFeePerGas":"0x0","maxFeePerGas":"0x0","gas":"0x0","input":null,"type":"0x2","chainId":"0x1","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":[]}]}""")] public void can_serialize_accessList_with_empty_storageKeys(TxType txType, string txJson) { - AccessList.Builder builder = new AccessList.Builder() - .AddAddress(TestItem.AddressA); + AccessList accessList = new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .Build(); Transaction transaction = new() { Type = txType, - AccessList = builder.Build(), + AccessList = accessList, }; - TransactionForRpc transactionForRpc = new(transaction); - + var transactionForRpc = TransactionForRpc.FromTransaction(transaction); string serialized = _serializer.Serialize(transactionForRpc); - JToken.Parse(serialized).Should().BeEquivalentTo(JToken.Parse(txJson)); + var actual = JObject.Parse(serialized).Property("accessList"); + var expected = JObject.Parse(txJson).Property("accessList"); + actual.Should().BeEquivalentTo(expected); } [Test] public void can_deserialize_accessList_with_empty_storageKeys() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":[]}]}"""; - object[] accessList = { new AccessListItemForRpc(TestItem.AddressA, new HashSet()) }; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.AccessList); - transactionForRpc.AccessList.Should().BeEquivalentTo(accessList); + + AccessList expected = new AccessList.Builder().AddAddress(TestItem.AddressA).Build(); + AccessList actual = ((AccessListTransactionForRpc)transactionForRpc).AccessList!.ToAccessList(); + expected.Should().BeEquivalentTo(actual); } [Test] public void can_deserialize_accessList_with_null_storageKeys() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":null,"type":"0x01","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099"}]}"""; - object[] accessList = { new AccessListItemForRpc(TestItem.AddressA, null) }; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.AccessList); - transactionForRpc.AccessList.Should().BeEquivalentTo(accessList); + + AccessList expected = new AccessList.Builder().AddAddress(TestItem.AddressA).Build(); + AccessList actual = ((AccessListTransactionForRpc)transactionForRpc).AccessList!.ToAccessList(); + expected.Should().BeEquivalentTo(actual); } [Test] public void can_deserialize_accessList_with_null_storageKeys_and_eip1559_txType() { string json = """{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":null,"value":"0x0","maxFeePerGas":"0x10","gas":"0x0","input":null,"type":"0x02","accessList":[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099"}]}"""; - AccessListItemForRpc[] accessList = { new(TestItem.AddressA, null) }; - TransactionForRpc transactionForRpc = _serializer.Deserialize(json); transactionForRpc.Type.Should().Be(TxType.EIP1559); - transactionForRpc.AccessList.Should().BeEquivalentTo(accessList); + + AccessList expected = new AccessList.Builder().AddAddress(TestItem.AddressA).Build(); + AccessList actual = ((EIP1559TransactionForRpc)transactionForRpc).AccessList!.ToAccessList(); + expected.Should().BeEquivalentTo(actual); } [Test] @@ -208,8 +215,8 @@ public void can_deserialize_not_provided_txType() TransactionForRpc transactionForRpc = _serializer.Deserialize(json); - // if there is not TxType provided, default value should be TxType.Legacy equal 0 - transactionForRpc.Type.Should().Be(0); + // If there is not `TxType` provided, default value should be `TxType.Legacy` + transactionForRpc.Type.Should().Be(TxType.Legacy); } [Test] @@ -219,8 +226,8 @@ public void can_deserialize_direct_null_txType() TransactionForRpc transactionForRpc = _serializer.Deserialize(json); - // if there is null TxType provided, still default value should be TxType.Legacy equal 0 - transactionForRpc.Type.Should().Be(0); + // If there is not `TxType` provided, default value should be `TxType.Legacy` + transactionForRpc.Type.Should().Be(TxType.Legacy); } [TestCase(TxType.AccessList)] @@ -231,8 +238,11 @@ public void can_convert_fromTransaction_toTransactionForRpc_and_back(TxType txTy { Type = txType, AccessList = GetTestAccessList(), + ChainId = BlockchainIds.Mainnet, + SenderAddress = Address.SystemUser, + Data = Memory.Empty, }; - TransactionForRpc transactionForRpc = new(transaction); + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(transaction); Transaction afterConversion = transactionForRpc.ToTransaction(); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs index 272ad1d2a8e..c8e1ac1dabd 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.IO.Abstractions; -using System.Linq; -using System.Reflection; using System.Text.Json; using FluentAssertions; @@ -13,13 +11,12 @@ using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Int256; -using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.JsonRpc.Modules.Net; @@ -56,19 +53,19 @@ public void TearDown() private ILogManager _logManager = null!; private JsonRpcContext _context = null!; - private JsonRpcResponse TestRequest(T module, string method, params string[]? parameters) where T : IRpcModule + private JsonRpcResponse TestRequest(T module, string method, params object?[]? parameters) where T : IRpcModule { var pool = new SingletonModulePool(new SingletonFactory(module), true); return TestRequestWithPool(pool, method, parameters); } - private JsonRpcResponse TestRequestWithPool(IRpcModulePool pool, string method, params string[]? parameters) where T : IRpcModule + private JsonRpcResponse TestRequestWithPool(IRpcModulePool pool, string method, params object?[]? parameters) where T : IRpcModule { RpcModuleProvider moduleProvider = new(new FileSystem(), _configurationProvider.GetConfig(), LimboLogs.Instance); moduleProvider.Register(pool); _jsonRpcService = new JsonRpcService(moduleProvider, _logManager, _configurationProvider.GetConfig()); - JsonRpcRequest request = RpcTest.GetJsonRequest(method, parameters); + JsonRpcRequest request = RpcTest.BuildJsonRequest(method, parameters); JsonRpcResponse response = _jsonRpcService.SendRequestAsync(request, _context).Result; Assert.That(response.Id, Is.EqualTo(request.Id)); return response; @@ -111,11 +108,9 @@ public void CanRunEthSimulateV1Empty() [Test] public void CanHandleOptionalArguments() { - EthereumJsonSerializer serializer = new(); - string serialized = serializer.Serialize(new TransactionForRpc()); IEthRpcModule ethRpcModule = Substitute.For(); ethRpcModule.eth_call(Arg.Any()).ReturnsForAnyArgs(_ => ResultWrapper.Success("0x1")); - JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", serialized) as JsonRpcSuccessResponse; + JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", new LegacyTransactionForRpc()) as JsonRpcSuccessResponse; Assert.That(response?.Result, Is.EqualTo("0x1")); } @@ -175,13 +170,10 @@ public void GetNewFilterTest() [Test] public void Eth_call_is_working_with_implicit_null_as_the_last_argument() { - EthereumJsonSerializer serializer = new(); IEthRpcModule ethRpcModule = Substitute.For(); ethRpcModule.eth_call(Arg.Any(), Arg.Any()).ReturnsForAnyArgs(x => ResultWrapper.Success("0x")); - string serialized = serializer.Serialize(new TransactionForRpc()); - - JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", serialized) as JsonRpcSuccessResponse; + JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", new LegacyTransactionForRpc()) as JsonRpcSuccessResponse; Assert.That(response?.Result, Is.EqualTo("0x")); } @@ -189,13 +181,10 @@ public void Eth_call_is_working_with_implicit_null_as_the_last_argument() [TestCase(null)] public void Eth_call_is_working_with_explicit_null_as_the_last_argument(string? nullValue) { - EthereumJsonSerializer serializer = new(); IEthRpcModule ethRpcModule = Substitute.For(); ethRpcModule.eth_call(Arg.Any(), Arg.Any()).ReturnsForAnyArgs(x => ResultWrapper.Success("0x")); - string serialized = serializer.Serialize(new TransactionForRpc()); - - JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", serialized, nullValue!) as JsonRpcSuccessResponse; + JsonRpcSuccessResponse? response = TestRequest(ethRpcModule, "eth_call", new LegacyTransactionForRpc(), nullValue) as JsonRpcSuccessResponse; Assert.That(response?.Result, Is.EqualTo("0x")); } @@ -204,17 +193,13 @@ public void Eth_getTransactionReceipt_properly_fails_given_wrong_parameters() { IEthRpcModule ethRpcModule = Substitute.For(); - string[] parameters = { - """["0x80757153e93d1b475e203406727b62a501187f63e23b8fa999279e219ee3be71"]""" - }; - JsonRpcResponse response = TestRequest(ethRpcModule, "eth_getTransactionReceipt", parameters); - - response.Should() - .BeAssignableTo() - .Which - .Error.Should().NotBeNull(); - Error error = (response as JsonRpcErrorResponse)!.Error!; - error.Code.Should().Be(ErrorCodes.InvalidParams); + string[] parameters = + [ + """["0x80757153e93d1b475e203406727b62a501187f63e23b8fa999279e219ee3be71"]""" + ]; + JsonRpcErrorResponse? response = TestRequest(ethRpcModule, "eth_getTransactionReceipt", parameters) as JsonRpcErrorResponse; + + Assert.That(response?.Error?.Code, Is.EqualTo(ErrorCodes.InvalidParams)); } [Test] @@ -263,13 +248,10 @@ public void BlockForRpc_should_expose_withdrawals_if_any((bool Expected, Block B new[] { (true, Build.A.Block - .WithWithdrawals(new[] - { - Build.A.Withdrawal - .WithAmount(1) - .WithRecipient(TestItem.AddressA) - .TestObject - }) + .WithWithdrawals(Build.A.Withdrawal + .WithAmount(1) + .WithRecipient(TestItem.AddressA) + .TestObject) .TestObject ), diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs index 34b6648bf5e..e011481dbb2 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs @@ -80,7 +80,7 @@ static async Task CountNumberOfMessages(Socket socket, CancellationToken to byte[] buffer = new byte[10]; while (true) { - ReceiveResult? result = await stream.ReceiveAsync(buffer); + ReceiveResult? result = await stream.ReceiveAsync(buffer).ConfigureAwait(false); if (result?.EndOfMessage == true) { messages++; @@ -108,7 +108,7 @@ static async Task CountNumberOfMessages(Socket socket, CancellationToken to Task sendMessages = Task.Run(async () => { using Socket socket = new(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(ipEndPoint); + await socket.ConnectAsync(ipEndPoint).ConfigureAwait(false); using IpcSocketMessageStream stream = new(socket); using JsonRpcSocketsClient client = new( @@ -124,17 +124,17 @@ static async Task CountNumberOfMessages(Socket socket, CancellationToken to for (int i = 0; i < messageCount; i++) { using JsonRpcResult result = JsonRpcResult.Single(RandomSuccessResponse(1_000, () => disposeCount++), default); - await client.SendJsonRpcResult(result); - await Task.Delay(10); + await client.SendJsonRpcResult(result).ConfigureAwait(false); + await Task.Delay(1).ConfigureAwait(false); } disposeCount.Should().Be(messageCount); - await cts.CancelAsync(); + await cts.CancelAsync().ConfigureAwait(false); return messageCount; }); - await Task.WhenAll(sendMessages, receiveMessages); + await Task.WhenAll(sendMessages, receiveMessages).ConfigureAwait(false); int sent = sendMessages.Result; int received = receiveMessages.Result; @@ -158,7 +158,7 @@ async Task ReadMessages(Socket socket, IList receivedMessages, Canc byte[] buffer = new byte[bufferSize]; while (true) { - ReceiveResult? result = await stream.ReceiveAsync(buffer); + ReceiveResult? result = await stream.ReceiveAsync(buffer).ConfigureAwait(false); if (result is not null) { msg.AddRange(buffer.Take(result.Read)); @@ -190,14 +190,14 @@ async Task ReadMessages(Socket socket, IList receivedMessages, Canc Task receiveMessages = OneShotServer( ipEndPoint, - async socket => await ReadMessages(socket, receivedMessages, cts.Token) + async socket => await ReadMessages(socket, receivedMessages, cts.Token).ConfigureAwait(false) ); Task sendMessages = Task.Run(async () => { int messageCount = 0; using Socket socket = new(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(ipEndPoint); + await socket.ConnectAsync(ipEndPoint).ConfigureAwait(false); using IpcSocketMessageStream stream = new(socket); using JsonRpcSocketsClient client = new( @@ -216,20 +216,20 @@ async Task ReadMessages(Socket socket, IList receivedMessages, Canc messageCount++; var msg = Enumerable.Range(11, i).Select(x => (byte)x).ToArray(); sentMessages.Add(msg); - await stream.WriteAsync(msg); - await stream.WriteEndOfMessageAsync(); + await stream.WriteAsync(msg).ConfigureAwait(false); + await stream.WriteEndOfMessageAsync().ConfigureAwait(false); if (i % 10 == 0) { - await Task.Delay(10); + await Task.Delay(1).ConfigureAwait(false); } } stream.Close(); - await cts.CancelAsync(); + await cts.CancelAsync().ConfigureAwait(false); return messageCount; }); - await Task.WhenAll(sendMessages, receiveMessages); + await Task.WhenAll(sendMessages, receiveMessages).ConfigureAwait(false); int sent = sendMessages.Result; int received = receiveMessages.Result; diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs index 92f84ca3e9f..dc40547d637 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs @@ -96,8 +96,8 @@ public async Task Smoke_test_peers() { string unused0 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_addPeer", _enodeString); string unused1 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_removePeer", _enodeString); - string unused2 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_addPeer", _enodeString, "true"); - string unused3 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_removePeer", _enodeString, "true"); + string unused2 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_addPeer", _enodeString, true); + string unused3 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_removePeer", _enodeString, true); string unused4 = await RpcTest.TestSerializedRequest(_adminRpcModule, "admin_peers"); } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs index 234acd6e729..fed0e3ccfba 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs @@ -19,7 +19,7 @@ using Nethermind.Db; using Nethermind.Evm.Tracing.GethStyle; using Nethermind.Evm.Tracing.GethStyle.Custom; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; using Nethermind.JsonRpc.Modules.DebugModule; using Nethermind.JsonRpc.Modules.Eth; @@ -50,8 +50,7 @@ public async Task Get_from_db() IConfigProvider configProvider = Substitute.For(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); using var response = - await RpcTest.TestRequest(rpcModule, "debug_getFromDb", "STATE", key.ToHexString(true)) as - JsonRpcSuccessResponse; + await RpcTest.TestRequest(rpcModule, "debug_getFromDb", "STATE", key) as JsonRpcSuccessResponse; byte[]? result = response?.Result as byte[]; } @@ -65,15 +64,15 @@ public async Task Get_from_db_null_value() DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); byte[] key = new byte[] { 1, 2, 3 }; using var response = - await RpcTest.TestRequest(rpcModule, "debug_getFromDb", "STATE", key.ToHexString(true)) as + await RpcTest.TestRequest(rpcModule, "debug_getFromDb", "STATE", key) as JsonRpcSuccessResponse; Assert.That(response, Is.Not.Null); } - [TestCase("1")] - [TestCase("0x1")] - public async Task Get_chain_level(string parameter) + [TestCase(1)] + [TestCase(0x1)] + public async Task Get_chain_level(object parameter) { debugBridge.GetLevelInfo(1).Returns( new ChainLevelInfo( @@ -100,7 +99,7 @@ public async Task Get_block_rlp_by_hash() debugBridge.GetBlockRlp(new BlockParameter(Keccak.Zero)).Returns(rlp.Bytes); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlpByHash", $"{Keccak.Zero.Bytes.ToHexString()}") as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlpByHash", Keccak.Zero) as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -113,7 +112,7 @@ public async Task Get_raw_Header() debugBridge.GetBlock(new BlockParameter((long)0)).Returns(blk); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawHeader", $"{Keccak.Zero.Bytes.ToHexString()}") as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawHeader", 0) as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -126,7 +125,7 @@ public async Task Get_block_rlp() localDebugBridge.GetBlockRlp(new BlockParameter(1)).Returns(rlp.Bytes); DebugRpcModule rpcModule = new(LimboLogs.Instance, localDebugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlp", "1") as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlp", 1) as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -140,7 +139,7 @@ public async Task Get_rawblock() localDebugBridge.GetBlockRlp(new BlockParameter(1)).Returns(rlp.Bytes); DebugRpcModule rpcModule = new(LimboLogs.Instance, localDebugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", "1") as JsonRpcSuccessResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", 1) as JsonRpcSuccessResponse; Assert.That((byte[]?)response?.Result, Is.EqualTo(rlp.Bytes)); } @@ -151,7 +150,7 @@ public async Task Get_block_rlp_when_missing() debugBridge.GetBlockRlp(new BlockParameter(1)).ReturnsNull(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlp", "1") as JsonRpcErrorResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlp", 1) as JsonRpcErrorResponse; Assert.That(response?.Error?.Code, Is.EqualTo(-32001)); } @@ -162,7 +161,7 @@ public async Task Get_rawblock_when_missing() debugBridge.GetBlockRlp(new BlockParameter(1)).ReturnsNull(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", "1") as JsonRpcErrorResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getRawBlock", 1) as JsonRpcErrorResponse; Assert.That(response?.Error?.Code, Is.EqualTo(-32001)); } @@ -175,7 +174,7 @@ public async Task Get_block_rlp_by_hash_when_missing() debugBridge.GetBlockRlp(new BlockParameter(Keccak.Zero)).ReturnsNull(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlpByHash", $"{Keccak.Zero.Bytes.ToHexString()}") as JsonRpcErrorResponse; + using var response = await RpcTest.TestRequest(rpcModule, "debug_getBlockRlpByHash", Keccak.Zero) as JsonRpcErrorResponse; Assert.That(response?.Error?.Code, Is.EqualTo(-32001)); } @@ -213,7 +212,7 @@ public async Task Get_trace() debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Any()).Returns(trace); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{}"); + string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA, new object()); Assert.That(response, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"gas\":\"0x0\",\"failed\":false,\"returnValue\":\"0xa2\",\"structLogs\":[{\"pc\":0,\"op\":\"STOP\",\"gas\":22000,\"gasCost\":1,\"depth\":1,\"error\":null,\"stack\":[\"0000000000000000000000000000000000000000000000000000000000000007\",\"0000000000000000000000000000000000000000000000000000000000000008\"],\"memory\":[\"0000000000000000000000000000000000000000000000000000000000000005\",\"0000000000000000000000000000000000000000000000000000000000000006\"],\"storage\":{\"0000000000000000000000000000000000000000000000000000000000000001\":\"0000000000000000000000000000000000000000000000000000000000000002\",\"0000000000000000000000000000000000000000000000000000000000000003\":\"0000000000000000000000000000000000000000000000000000000000000004\"}}]},\"id\":67}")); } @@ -226,7 +225,7 @@ public async Task Get_js_trace() debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Any()).Returns(trace); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{}"); + string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA, new object()); Assert.That(response, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"customProperty\":1},\"id\":67}")); } @@ -262,7 +261,7 @@ public async Task Get_trace_with_options() debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Any()).Returns(trace); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{\"disableStack\" : true}"); + string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA, new { disableStack = true }); Assert.That(response, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"gas\":\"0x0\",\"failed\":false,\"returnValue\":\"0xa2\",\"structLogs\":[{\"pc\":0,\"op\":\"STOP\",\"gas\":22000,\"gasCost\":1,\"depth\":1,\"error\":null,\"stack\":[],\"memory\":[\"0000000000000000000000000000000000000000000000000000000000000005\",\"0000000000000000000000000000000000000000000000000000000000000006\"],\"storage\":{\"0000000000000000000000000000000000000000000000000000000000000001\":\"0000000000000000000000000000000000000000000000000000000000000002\",\"0000000000000000000000000000000000000000000000000000000000000003\":\"0000000000000000000000000000000000000000000000000000000000000004\"}}]},\"id\":67}")); } @@ -314,7 +313,7 @@ public async Task Get_trace_with_javascript_setup() debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Do(arg => passedOption = arg)) .Returns(new GethLikeTxTrace()); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA.ToString(true), "{\"disableStack\" : true, \"tracerConfig\" : {\"a\":true} }"); + await RpcTest.TestSerializedRequest(rpcModule, "debug_traceTransaction", TestItem.KeccakA, new { disableStack = true, tracerConfig = new { a = true } }); passedOption.TracerConfig!.ToString().Should().Be("{\"a\":true}"); } @@ -348,7 +347,7 @@ public void Debug_traceCall_test() GethTraceOptions gtOptions = new(); Transaction transaction = Build.A.Transaction.WithTo(TestItem.AddressA).WithHash(TestItem.KeccakA).TestObject; - TransactionForRpc txForRpc = new(transaction); + TransactionForRpc txForRpc = TransactionForRpc.FromTransaction(transaction); debugBridge.GetTransactionTrace(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()).Returns(trace); @@ -399,7 +398,7 @@ public async Task Migrate_receipts() { debugBridge.MigrateReceipts(Arg.Any()).Returns(true); IDebugRpcModule rpcModule = new DebugRpcModule(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_migrateReceipts", "100"); + string response = await RpcTest.TestSerializedRequest(rpcModule, "debug_migrateReceipts", 100); Assert.That(response, Is.Not.Null); } @@ -408,7 +407,7 @@ public async Task Update_head_block() { debugBridge.UpdateHeadBlock(Arg.Any()); IDebugRpcModule rpcModule = new DebugRpcModule(LimboLogs.Instance, debugBridge, jsonRpcConfig, specProvider); - await RpcTest.TestSerializedRequest(rpcModule, "debug_resetHead", TestItem.KeccakA.ToString()); + await RpcTest.TestSerializedRequest(rpcModule, "debug_resetHead", TestItem.KeccakA); debugBridge.Received().UpdateHeadBlock(TestItem.KeccakA); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs index 0bc1a37fb09..1087a3534c9 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs @@ -9,9 +9,7 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; using Nethermind.Evm; -using Nethermind.Evm.Tracing; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; @@ -32,7 +30,7 @@ public async Task Eth_estimateGas_web3_should_return_insufficient_balance_error( TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient funds for transfer: address 0x0001020304050607080910111213141516171819\"},\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); @@ -47,7 +45,7 @@ public async Task Eth_estimateGas_web3_sample_not_enough_gas_system_account() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(Address.SystemUser).Should().BeFalse(); } @@ -61,7 +59,7 @@ public async Task Eth_estimateGas_web3_sample_not_enough_gas_other_account() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } @@ -75,7 +73,7 @@ public async Task Eth_estimateGas_web3_above_block_gas_limit() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gas\":\"0x100000000\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } @@ -88,17 +86,17 @@ public async Task Eth_create_access_list_calculates_proper_gas(bool optimize, lo var test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] accessList) = GetTestAccessList(loads); + (byte[] code, _) = GetTestAccessList(loads); - TransactionForRpc transaction = - test.JsonSerializer.Deserialize( + AccessListTransactionForRpc transaction = + test.JsonSerializer.Deserialize( $"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); string serializedCreateAccessList = await test.TestEthRpc("eth_createAccessList", - test.JsonSerializer.Serialize(transaction), "0x0", optimize.ToString().ToLower()); + transaction, "0x0", optimize.ToString().ToLower()); - transaction.AccessList = test.JsonSerializer.Deserialize(JToken.Parse(serializedCreateAccessList).SelectToken("result.accessList")!.ToString()); + transaction.AccessList = test.JsonSerializer.Deserialize(JToken.Parse(serializedCreateAccessList).SelectToken("result.accessList")!.ToString()); string serializedEstimateGas = - await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); var gasUsedEstimateGas = JToken.Parse(serializedEstimateGas).Value("result"); var gasUsedCreateAccessList = @@ -117,17 +115,17 @@ public async Task Eth_estimate_gas_with_accessList(bool senderAccessList, long g var test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithConfig(new JsonRpcConfig() { EstimateErrorMargin = 0 }) .Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] accessList) = GetTestAccessList(2, senderAccessList); + (byte[] code, AccessListForRpc accessList) = GetTestAccessList(2, senderAccessList); - TransactionForRpc transaction = - test.JsonSerializer.Deserialize( + AccessListTransactionForRpc transaction = + test.JsonSerializer.Deserialize( $"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); - string serialized = await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + string serialized = await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); Assert.That( serialized, Is.EqualTo($"{{\"jsonrpc\":\"2.0\",\"result\":\"{gasPriceWithoutAccessList.ToHexString(true)}\",\"id\":67}}")); transaction.AccessList = accessList; - serialized = await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + serialized = await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); Assert.That( serialized, Is.EqualTo($"{{\"jsonrpc\":\"2.0\",\"result\":\"{gasPriceWithAccessList.ToHexString(true)}\",\"id\":67}}")); } @@ -138,18 +136,18 @@ public async Task Eth_estimate_gas_is_lower_with_optimized_access_list() var test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] accessList) = GetTestAccessList(2, true); - (byte[] _, AccessListItemForRpc[] optimizedAccessList) = GetTestAccessList(2, false); + (byte[] code, AccessListForRpc accessList) = GetTestAccessList(2, true); + (byte[] _, AccessListForRpc optimizedAccessList) = GetTestAccessList(2, false); - TransactionForRpc transaction = - test.JsonSerializer.Deserialize( + AccessListTransactionForRpc transaction = + test.JsonSerializer.Deserialize( $"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); transaction.AccessList = accessList; - string serialized = await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + string serialized = await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); long estimateGas = Convert.ToInt64(JToken.Parse(serialized).Value("result"), 16); transaction.AccessList = optimizedAccessList; - serialized = await test.TestEthRpc("eth_estimateGas", test.JsonSerializer.Serialize(transaction), "0x0"); + serialized = await test.TestEthRpc("eth_estimateGas", transaction, "0x0"); long optimizedEstimateGas = Convert.ToInt64(JToken.Parse(serialized).Value("result"), 16); optimizedEstimateGas.Should().BeLessThan(estimateGas); @@ -161,7 +159,7 @@ public async Task Estimate_gas_without_gas_pricing() using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); } @@ -171,7 +169,7 @@ public async Task Estimate_gas_with_gas_pricing() using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); } @@ -181,7 +179,7 @@ public async Task Estimate_gas_without_gas_pricing_after_1559_legacy() using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); } @@ -191,7 +189,7 @@ public async Task Estimate_gas_without_gas_pricing_after_1559_new_type_of_transa using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); byte[] code = Prepare.EvmCode .Op(Instruction.BASEFEE) @@ -217,7 +215,7 @@ public async Task Estimate_gas_with_base_fee_opcode() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0xe891\",\"id\":67}")); } @@ -236,7 +234,7 @@ public async Task Estimate_gas_with_revert() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); - string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_estimateGas", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"revert\"},\"id\":67}")); } @@ -249,12 +247,14 @@ public async Task should_estimate_transaction_with_deployed_code_when_eip3607_en using Context ctx = await Context.Create(specProvider); Transaction tx = Build.A.Transaction.SignedAndResolved(TestItem.PrivateKeyA).TestObject; - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, tx); + LegacyTransactionForRpc transaction = new LegacyTransactionForRpc(tx, 1, Keccak.Zero, 1L) + { + To = TestItem.AddressB + }; ctx.Test.State.InsertCode(TestItem.AddressA, "H"u8.ToArray(), London.Instance); - transaction.To = TestItem.AddressB; string serialized = - await ctx.Test.TestEthRpc("eth_estimateGas", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_estimateGas", transaction, "latest"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}")); } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs index 060c4e855ad..adbf92f2ca3 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs @@ -8,7 +8,7 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; using Nethermind.Evm; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; @@ -26,7 +26,7 @@ public async Task Eth_call_web3_sample() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "0x0"); + await ctx.Test.TestEthRpc("eth_call", transaction, "0x0"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -38,7 +38,7 @@ public async Task Eth_call_web3_sample_not_enough_gas_system_account() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "0x0"); + await ctx.Test.TestEthRpc("eth_call", transaction, "0x0"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(Address.SystemUser).Should().BeFalse(); } @@ -51,7 +51,7 @@ public async Task Eth_call_web3_should_return_insufficient_balance_error() ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient funds for transfer: address 0x0001020304050607080910111213141516171819\"},\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); @@ -66,7 +66,7 @@ public async Task Eth_call_web3_sample_not_enough_gas_other_account() TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "0x0"); + await ctx.Test.TestEthRpc("eth_call", transaction, "0x0"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); ctx.Test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } @@ -75,11 +75,13 @@ public async Task Eth_call_web3_sample_not_enough_gas_other_account() public async Task Eth_call_no_sender() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.To = TestItem.AddressB; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + To = TestItem.AddressB + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -87,12 +89,14 @@ public async Task Eth_call_no_sender() public async Task Eth_call_no_recipient_should_work_as_init() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - transaction.Input = new byte[] { 1, 2, 3 }; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA, + Input = [1, 2, 3] + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"StackUnderflow\"},\"id\":67}")); } @@ -106,12 +110,14 @@ public async Task should_not_reject_transactions_with_deployed_code_when_eip3607 using Context ctx = await Context.Create(specProvider); Transaction tx = Build.A.Transaction.SignedAndResolved(TestItem.PrivateKeyA).TestObject; - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, tx); + LegacyTransactionForRpc transaction = new(tx, 1, Keccak.Zero, 1L) + { + To = TestItem.AddressB + }; ctx.Test.State.InsertCode(TestItem.AddressA, "H"u8.ToArray(), London.Instance); - transaction.To = TestItem.AddressB; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -129,12 +135,14 @@ public async Task Eth_call_ethereum_recipient() public async Task Eth_call_ok() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - transaction.To = TestItem.AddressB; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA, + To = TestItem.AddressB + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -142,12 +150,14 @@ public async Task Eth_call_ok() public async Task Eth_call_with_blockhash_ok() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - transaction.To = TestItem.AddressB; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA, + To = TestItem.AddressB + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "{\"blockHash\":\"0xf0b3f69cbd4e1e8d9b0ef02ff5d1384d18e19d251a4052f5f90bab190c5e8937\"}"); + await ctx.Test.TestEthRpc("eth_call", transaction, "{\"blockHash\":\"0xf0b3f69cbd4e1e8d9b0ef02ff5d1384d18e19d251a4052f5f90bab190c5e8937\"}"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32001,\"message\":\"0xf0b3f69cbd4e1e8d9b0ef02ff5d1384d18e19d251a4052f5f90bab190c5e8937 could not be found\"},\"id\":67}")); } @@ -155,11 +165,12 @@ public async Task Eth_call_with_blockhash_ok() public async Task Eth_call_create_tx_with_empty_data() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA + }; string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); serialized.Should().BeEquivalentTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"Contract creation without any data provided.\"},\"id\":67}"); } @@ -167,15 +178,17 @@ public async Task Eth_call_create_tx_with_empty_data() public async Task Eth_call_missing_state_after_fast_sync() { using Context ctx = await Context.Create(); - TransactionForRpc transaction = new(Keccak.Zero, 1L, 1, new Transaction()); - transaction.From = TestItem.AddressA; - transaction.To = TestItem.AddressB; + LegacyTransactionForRpc transaction = new(new Transaction(), 1, Keccak.Zero, 1L) + { + From = TestItem.AddressA, + To = TestItem.AddressB + }; ctx.Test.StateDb.Clear(); ctx.Test.TrieStore.ClearCache(); string serialized = - await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction), "latest"); + await ctx.Test.TestEthRpc("eth_call", transaction, "latest"); serialized.Should().StartWith("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32002,"); } @@ -185,14 +198,14 @@ public async Task Eth_call_with_accessList() var test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] accessList) = GetTestAccessList(); + (byte[] code, AccessListForRpc accessList) = GetTestAccessList(); - TransactionForRpc transaction = - test.JsonSerializer.Deserialize( + AccessListTransactionForRpc transaction = + test.JsonSerializer.Deserialize( $"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); transaction.AccessList = accessList; - string serialized = await test.TestEthRpc("eth_call", test.JsonSerializer.Serialize(transaction), "0x0"); + string serialized = await test.TestEthRpc("eth_call", transaction, "0x0"); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x010203\",\"id\":67}")); } @@ -202,7 +215,7 @@ public async Task Eth_call_without_gas_pricing() using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -212,7 +225,7 @@ public async Task Eth_call_with_gas_pricing() using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -222,7 +235,7 @@ public async Task Eth_call_without_gas_pricing_after_1559_legacy() using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); } @@ -232,7 +245,7 @@ public async Task Eth_call_without_gas_pricing_after_1559_new_type_of_transactio using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\"}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}")); byte[] code = Prepare.EvmCode .Op(Instruction.BASEFEE) @@ -258,7 +271,7 @@ public async Task Eth_call_with_base_fee_opcode_should_return_0() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"id\":67}")); } @@ -277,7 +290,7 @@ public async Task Eth_call_with_revert() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx.Test.JsonSerializer.Deserialize( $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); - string serialized = await ctx.Test.TestEthRpc("eth_call", ctx.Test.JsonSerializer.Serialize(transaction)); + string serialized = await ctx.Test.TestEthRpc("eth_call", transaction); Assert.That( serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"revert\"},\"id\":67}")); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs index 843406aad94..6f5bab4328c 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs @@ -8,12 +8,14 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using FluentAssertions.Json; using Nethermind.Blockchain; using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Eip2930; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Core.Test.Blockchain; @@ -22,6 +24,7 @@ using Nethermind.Evm; using Nethermind.Facade; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Filters; using Nethermind.Int256; using Nethermind.Serialization.Json; @@ -30,6 +33,7 @@ using Nethermind.Specs.Forks; using Nethermind.Specs.Test; using Nethermind.TxPool; +using Newtonsoft.Json.Linq; using NSubstitute; using NSubstitute.ExceptionExtensions; using NUnit.Framework; @@ -73,7 +77,7 @@ public async Task Eth_get_transaction_by_block_hash_and_index() { using Context ctx = await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockHashAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Hash!.ToString(), "1"); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"hash\":\"0x7126cf20a0ad8bd51634837d9049615c34c1bff5e1a54e5663f7e23109bff48b\",\"nonce\":\"0x2\",\"blockHash\":\"0x29f141925d2d8e357ae5b6040c97aa12d7ac6dfcbe2b20e7b616d8907ac8e1f3\",\"blockNumber\":\"0x3\",\"transactionIndex\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\",\"v\":\"0x25\",\"s\":\"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb\",\"r\":\"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd\"},\"id\":67}"), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo("""{"jsonrpc":"2.0","result":{"hash":"0x7126cf20a0ad8bd51634837d9049615c34c1bff5e1a54e5663f7e23109bff48b","nonce":"0x2","blockHash":"0x29f141925d2d8e357ae5b6040c97aa12d7ac6dfcbe2b20e7b616d8907ac8e1f3","blockNumber":"0x3","transactionIndex":"0x1","from":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","to":"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","v":"0x25","s":"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb","r":"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd"},"id":67}"""); } [Test] @@ -81,7 +85,7 @@ public async Task Eth_get_transaction_by_hash() { using Context ctx = await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByHash", ctx.Test.BlockTree.FindHeadBlock()!.Transactions.Last().Hash!.ToString()); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"hash\":\"0x7126cf20a0ad8bd51634837d9049615c34c1bff5e1a54e5663f7e23109bff48b\",\"nonce\":\"0x2\",\"blockHash\":\"0x29f141925d2d8e357ae5b6040c97aa12d7ac6dfcbe2b20e7b616d8907ac8e1f3\",\"blockNumber\":\"0x3\",\"transactionIndex\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\",\"v\":\"0x25\",\"s\":\"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb\",\"r\":\"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd\"},\"id\":67}"), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo("""{"jsonrpc":"2.0","result":{"hash":"0x7126cf20a0ad8bd51634837d9049615c34c1bff5e1a54e5663f7e23109bff48b","nonce":"0x2","blockHash":"0x29f141925d2d8e357ae5b6040c97aa12d7ac6dfcbe2b20e7b616d8907ac8e1f3","blockNumber":"0x3","transactionIndex":"0x1","from":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","to":"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","v":"0x25","s":"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb","r":"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd"},"id":67}"""); } [Test] @@ -107,7 +111,7 @@ public async Task Eth_pending_transactions() using Context ctx = await Context.Create(); ctx.Test.AddTransactions(Build.A.Transaction.SignedAndResolved(TestItem.PrivateKeyD).TestObject); string serialized = await ctx.Test.TestEthRpc("eth_pendingTransactions"); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":[{\"hash\":\"0x190d9a78dbc61b1856162ab909976a1b28ba4a41ee041341576ea69686cd3b29\",\"nonce\":\"0x0\",\"blockHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\",\"v\":\"0x26\",\"s\":\"0x2d04e55699fa32e6b65a22189f7571f5030d636d7d44a8b53fe016a2c3ecde24\",\"r\":\"0xda3978c3a1430bd902cf5bbca73c5a1eca019b3f003c95ee16657fd0bb89534c\"}],\"id\":67}"), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo("""{"jsonrpc":"2.0","result":[{"hash":"0x190d9a78dbc61b1856162ab909976a1b28ba4a41ee041341576ea69686cd3b29","nonce":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":null,"transactionIndex":null,"from":"0x475674cb523a0a2736b7f7534390288fce16982c","to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","v":"0x26","s":"0x2d04e55699fa32e6b65a22189f7571f5030d636d7d44a8b53fe016a2c3ecde24","r":"0xda3978c3a1430bd902cf5bbca73c5a1eca019b3f003c95ee16657fd0bb89534c"}],"id":67}"""); } [Test] @@ -115,9 +119,8 @@ public async Task Eth_pending_transactions_1559_tx() { using Context ctx = await Context.CreateWithLondonEnabled(); ctx.Test.AddTransactions(Build.A.Transaction.WithMaxPriorityFeePerGas(6.GWei()).WithMaxFeePerGas(11.GWei()).WithType(TxType.EIP1559).SignedAndResolved(TestItem.PrivateKeyC).TestObject); - const string addedTx = "\"hash\":\"0x7544f95c68426cb8a8a5a54889c60849ed96ff317835beb63b4d745cbc078cec\",\"nonce\":\"0x0\",\"blockHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x28fa6ae00\",\"maxPriorityFeePerGas\":\"0x165a0bc00\",\"maxFeePerGas\":\"0x28fa6ae00\",\"gas\":\"0x5208\",\"input\":\"0x\",\"chainId\":\"0x1\",\"type\":\"0x2\",\"accessList\":[],\"v\":\"0x0\",\"s\":\"0x606b869eab1c9d01ff462f887826cb8f349ea8f1b59d0635ae77155b3b84ad86\",\"r\":\"0x63b08cc0a06c88fb1dd79f273736b3463af12c6754f9df764aa222d2693a5d43\",\"yParity\":\"0x0\""; string serialized = await ctx.Test.TestEthRpc("eth_pendingTransactions"); - serialized.Contains(addedTx).Should().BeTrue(); + JToken.Parse(serialized).Should().ContainSubtree("""{"result": [{"hash":"0x7544f95c68426cb8a8a5a54889c60849ed96ff317835beb63b4d745cbc078cec","nonce":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":null,"transactionIndex":null,"from":"0x76e68a8696537e4141926f3e528733af9e237d69","to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x28fa6ae00","maxPriorityFeePerGas":"0x165a0bc00","maxFeePerGas":"0x28fa6ae00","gas":"0x5208","input":"0x","chainId":"0x1","type":"0x2","accessList":[],"v":"0x0","s":"0x606b869eab1c9d01ff462f887826cb8f349ea8f1b59d0635ae77155b3b84ad86","r":"0x63b08cc0a06c88fb1dd79f273736b3463af12c6754f9df764aa222d2693a5d43","yParity":"0x0"}]}"""); } [Test] @@ -125,9 +128,8 @@ public async Task Eth_pending_transactions_2930_tx() { using Context ctx = await Context.CreateWithLondonEnabled(); ctx.Test.AddTransactions(Build.A.Transaction.WithMaxPriorityFeePerGas(6.GWei()).WithMaxFeePerGas(11.GWei()).WithType(TxType.AccessList).SignedAndResolved(TestItem.PrivateKeyC).TestObject); - const string addedTx = "\"hash\":\"0x4eabe360dc515aadc8e35f75b23803bb86e7186ebf2e58412555b3d0c7750dcc\",\"nonce\":\"0x0\",\"blockHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"blockNumber\":null,\"transactionIndex\":null,\"from\":\"0x76e68a8696537e4141926f3e528733af9e237d69\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x165a0bc00\",\"gas\":\"0x5208\",\"input\":\"0x\",\"chainId\":\"0x1\",\"type\":\"0x1\",\"accessList\":[],\"v\":\"0x0\",\"s\":\"0x27e3dde7b07d6d6b50e0d11b29085036e9c8adc12dea52f6f07dd7a0551ff22a\",\"r\":\"0x619cb31fd4aa1c38ae36b31c5d8310f74d9f8ddd94389db91a68deb26737f2dc\",\"yParity\":\"0x0\""; string serialized = await ctx.Test.TestEthRpc("eth_pendingTransactions"); - serialized.Contains(addedTx).Should().BeTrue(); + JToken.Parse(serialized).Should().ContainSubtree("""{"result": [{"hash":"0x4eabe360dc515aadc8e35f75b23803bb86e7186ebf2e58412555b3d0c7750dcc","nonce":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":null,"transactionIndex":null,"from":"0x76e68a8696537e4141926f3e528733af9e237d69","to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x165a0bc00","gas":"0x5208","input":"0x","chainId":"0x1","type":"0x1","accessList":[],"v":"0x0","s":"0x27e3dde7b07d6d6b50e0d11b29085036e9c8adc12dea52f6f07dd7a0551ff22a","r":"0x619cb31fd4aa1c38ae36b31c5d8310f74d9f8ddd94389db91a68deb26737f2dc","yParity":"0x0"}]}"""); } [Test] @@ -144,7 +146,7 @@ public async Task Eth_get_transaction_by_block_number_and_index() { using Context ctx = await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getTransactionByBlockNumberAndIndex", ctx.Test.BlockTree.FindHeadBlock()!.Number.ToString(), "1"); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"hash\":\"0x7126cf20a0ad8bd51634837d9049615c34c1bff5e1a54e5663f7e23109bff48b\",\"nonce\":\"0x2\",\"blockHash\":\"0x29f141925d2d8e357ae5b6040c97aa12d7ac6dfcbe2b20e7b616d8907ac8e1f3\",\"blockNumber\":\"0x3\",\"transactionIndex\":\"0x1\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\",\"v\":\"0x25\",\"s\":\"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb\",\"r\":\"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd\"},\"id\":67}"), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo("""{"jsonrpc":"2.0","result":{"hash":"0x7126cf20a0ad8bd51634837d9049615c34c1bff5e1a54e5663f7e23109bff48b","nonce":"0x2","blockHash":"0x29f141925d2d8e357ae5b6040c97aa12d7ac6dfcbe2b20e7b616d8907ac8e1f3","blockNumber":"0x3","transactionIndex":"0x1","from":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","to":"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","v":"0x25","s":"0x575361bb330bf38b9a89dd8279d42a20d34edeaeede9739a7c2bdcbe3242d7bb","r":"0xe7c5ff3cba254c4fe8f9f12c3f202150bb9a0aebeee349ff2f4acb23585f56bd"},"id":67}"""); } [TestCase(false, "{\"jsonrpc\":\"2.0\",\"result\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0xa2a9f03b9493046696099d27b2612b99497aa1f392ec966716ab393c715a5bb6\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x0\",\"parentHash\":\"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x201\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"totalDifficulty\":\"0x0\",\"timestamp\":\"0xf4240\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]},\"id\":67}")] @@ -314,7 +316,7 @@ void handleNewBlock(object? sender, BlockEventArgs e) } test.BlockTree.NewHeadBlock += handleNewBlock; - using JsonRpcResponse newFilterResp = await RpcTest.TestRequest(test.EthRpcModule, "eth_newFilter", "{\"fromBlock\":\"latest\"}"); + using JsonRpcResponse newFilterResp = await RpcTest.TestRequest(test.EthRpcModule, "eth_newFilter", new { fromBlock = "latest" }); string getFilterLogsSerialized1 = await test.TestEthRpc("eth_getFilterChanges", (newFilterResp as JsonRpcSuccessResponse)!.Result?.ToString() ?? "0x0"); //expect empty - no changes so far @@ -512,7 +514,7 @@ void HandleNewBlock(object? sender, BlockReplacementEventArgs e) string getLogsSerialized = await test.TestEthRpc("eth_getLogs", $"{{\"fromBlock\":\"{blockHash}\"}}"); - using JsonRpcResponse? newFilterResp = await RpcTest.TestRequest(test.EthRpcModule, "eth_newFilter", $"{{\"fromBlock\":\"{blockHash}\"}}"); + using JsonRpcResponse? newFilterResp = await RpcTest.TestRequest(test.EthRpcModule, "eth_newFilter", new { fromBlock = blockHash }); Assert.That(newFilterResp is not null && newFilterResp is JsonRpcSuccessResponse, Is.True); @@ -647,7 +649,7 @@ public async Task Eth_get_block_by_hash_with_tx(string blockParameter, bool with { using Context ctx = await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getBlockByHash", ctx.Test.BlockTree.Head!.Hash!.ToString(), withTxData.ToString()); - Assert.That(serialized, Is.EqualTo(expectedResult), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo(expectedResult); } [TestCase(false, "earliest", "{\"jsonrpc\":\"2.0\",\"result\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0x2167088a0f0de66028d2b728235af6d467108c1750c3e11a8f6e6cd60fddb0e4\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x201\",\"stateRoot\":\"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f\",\"totalDifficulty\":\"0xf4240\",\"timestamp\":\"0xf4240\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]},\"id\":67}")] @@ -662,7 +664,7 @@ public async Task Eth_get_block_by_number(bool eip1559, string blockParameter, s { using Context ctx = eip1559 ? await Context.CreateWithLondonEnabled() : await Context.Create(); string serialized = await ctx.Test.TestEthRpc("eth_getBlockByNumber", blockParameter, "true"); - Assert.That(serialized, Is.EqualTo(expectedResult), serialized.Replace("\"", "\\\"")); + JToken.Parse(serialized).Should().BeEquivalentTo(expectedResult); } [TestCase("earliest", "{\"jsonrpc\":\"2.0\",\"result\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0x2167088a0f0de66028d2b728235af6d467108c1750c3e11a8f6e6cd60fddb0e4\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x201\",\"stateRoot\":\"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f\",\"totalDifficulty\":\"0xf4240\",\"timestamp\":\"0xf4240\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]},\"id\":67}")] @@ -816,11 +818,10 @@ public async Task Eth_get_block_by_number_with_recovering_sender_from_receipts() Block block = Build.A.Block.WithNumber(1) .WithStateRoot(new Hash256("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")) - .WithTransactions(new[] { Build.A.Transaction.TestObject }) + .WithTransactions(Build.A.Transaction.TestObject) .TestObject; - LogEntry[] entries = new[] - { + LogEntry[] entries = { Build.A.LogEntry.TestObject, Build.A.LogEntry.TestObject }; @@ -836,7 +837,7 @@ public async Task Eth_get_block_by_number_with_recovering_sender_from_receipts() ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockFinder(blockFinder).WithReceiptFinder(receiptFinder).Build(); string serialized = await ctx.Test.TestEthRpc("eth_getBlockByNumber", TestItem.KeccakA.ToString(), "true"); - Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0xe3026a6708b90d5cb25557ac38ddc3f5ef550af10f31e1cf771524da8553fa1c\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x1\",\"parentHash\":\"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x221\",\"stateRoot\":\"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f\",\"totalDifficulty\":\"0x0\",\"timestamp\":\"0xf4240\",\"transactions\":[{\"nonce\":\"0x0\",\"blockHash\":\"0xe3026a6708b90d5cb25557ac38ddc3f5ef550af10f31e1cf771524da8553fa1c\",\"blockNumber\":\"0x1\",\"transactionIndex\":\"0x0\",\"from\":\"0x2d36e6c27c34ea22620e7b7c45de774599406cf3\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\"}],\"transactionsRoot\":\"0x29cc403075ed3d1d6af940d577125cc378ee5a26f7746cbaf87f1cf4a38258b5\",\"uncles\":[]},\"id\":67}")); + JToken.Parse(serialized).Should().BeEquivalentTo("""{"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0xf4240","extraData":"0x010203","gasLimit":"0x3d0900","gasUsed":"0x0","hash":"0xe3026a6708b90d5cb25557ac38ddc3f5ef550af10f31e1cf771524da8553fa1c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2","nonce":"0x00000000000003e8","number":"0x1","parentHash":"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x221","stateRoot":"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f","totalDifficulty":"0x0","timestamp":"0xf4240","transactions":[{"nonce":"0x0","blockHash":"0xe3026a6708b90d5cb25557ac38ddc3f5ef550af10f31e1cf771524da8553fa1c","blockNumber":"0x1","transactionIndex":"0x0","from":"0x2d36e6c27c34ea22620e7b7c45de774599406cf3","to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","v":"0x0","r":"0x0","s":"0x0","hash":null}],"transactionsRoot":"0x29cc403075ed3d1d6af940d577125cc378ee5a26f7746cbaf87f1cf4a38258b5","uncles":[]},"id":67}"""); } [TestCase(false)] @@ -1071,10 +1072,9 @@ public async Task Send_transaction_without_signature_will_not_set_nonce_when_zer ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .WithBlockchainBridge(bridge).WithTxSender(txSender).Build(); - Transaction tx = Build.A.Transaction.TestObject; - TransactionForRpc rpcTx = new(tx); - rpcTx.Nonce = 0; - string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", new EthereumJsonSerializer().Serialize(rpcTx)); + Transaction tx = Build.A.Transaction.WithNonce(0).TestObject; + TransactionForRpc rpcTx = TransactionForRpc.FromTransaction(tx); + string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", rpcTx); // TODO: actual test missing now await txSender.Received().SendTransaction(Arg.Any(), TxHandlingOptions.PersistentBroadcast); Assert.That(serialized, Is.EqualTo($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}")); @@ -1092,9 +1092,9 @@ public async Task Send_transaction_without_signature_will_manage_nonce_when_null ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev) .WithBlockchainBridge(bridge).WithTxSender(txSender).Build(); Transaction tx = Build.A.Transaction.TestObject; - TransactionForRpc rpcTx = new(tx); + LegacyTransactionForRpc rpcTx = (LegacyTransactionForRpc)TransactionForRpc.FromTransaction(tx); rpcTx.Nonce = null; - string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", new EthereumJsonSerializer().Serialize(rpcTx)); + string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", rpcTx); await txSender.Received().SendTransaction(Arg.Any(), TxHandlingOptions.PersistentBroadcast | TxHandlingOptions.ManagedNonce); Assert.That(serialized, Is.EqualTo($"{{\"jsonrpc\":\"2.0\",\"result\":\"{TestItem.KeccakA.Bytes.ToHexString(true)}\",\"id\":67}}")); @@ -1105,9 +1105,9 @@ 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); + TransactionForRpc txForRpc = TransactionForRpc.FromTransaction(tx); - string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", new EthereumJsonSerializer().Serialize(txForRpc)); + string serialized = await ctx.Test.TestEthRpc("eth_sendTransaction", txForRpc); Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32010,\"message\":\"InsufficientFunds, Balance is zero, cannot pay gas\"},\"id\":67}")); } @@ -1139,16 +1139,16 @@ public async Task Eth_create_access_list_sample(AccessListProvided accessListPro { TestRpcBlockchain test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).Build(new TestSpecProvider(Berlin.Instance)); - (byte[] code, AccessListItemForRpc[] _) = GetTestAccessList(loads); + (byte[] code, AccessListForRpc _) = GetTestAccessList(loads); - TransactionForRpc transaction = test.JsonSerializer.Deserialize($"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); + AccessListTransactionForRpc transaction = test.JsonSerializer.Deserialize($"{{\"type\":\"0x1\", \"data\": \"{code.ToHexString(true)}\"}}"); if (accessListProvided != AccessListProvided.None) { transaction.AccessList = GetTestAccessList(2, accessListProvided == AccessListProvided.Full).AccessList; } - string serialized = await test.TestEthRpc("eth_createAccessList", test.JsonSerializer.Serialize(transaction), "0x0", optimize.ToString().ToLower()); + string serialized = await test.TestEthRpc("eth_createAccessList", transaction, "0x0", optimize); Assert.That(serialized, Is.EqualTo(expected)); } @@ -1156,13 +1156,36 @@ public async Task Eth_create_access_list_sample(AccessListProvided accessListPro [TestCase(0)] public static void Should_handle_gasCap_as_max_if_null_or_zero(long? gasCap) { - TransactionForRpc rpcTx = new TransactionForRpc(); + LegacyTransactionForRpc rpcTx = new LegacyTransactionForRpc(); rpcTx.EnsureDefaults(gasCap); Assert.That(rpcTx.Gas, Is.EqualTo(long.MaxValue), "Gas must be set to max if gasCap is null or 0"); } + [Ignore(reason: "Shows disparity across 'default' methods")] + [TestCase(null)] + [TestCase(0)] + public static void ToTransactionWithDefaults_and_EnsureDefaults_same_GasLimit(long? gasCap) + { + long toTransactionWitDefaultsGasLimit; + { + var rpcTx = new LegacyTransactionForRpc(); + Transaction tx = rpcTx.ToTransaction(); + toTransactionWitDefaultsGasLimit = tx.GasLimit; + } + + long ensureDefaultsGasLimit; + { + var rpcTx = new LegacyTransactionForRpc(); + rpcTx.EnsureDefaults(gasCap); + var tx = rpcTx.ToTransaction(); + ensureDefaultsGasLimit = tx.GasLimit; + } + + toTransactionWitDefaultsGasLimit.Should().Be(ensureDefaultsGasLimit); + } + [Test] public async Task eth_getBlockByNumber_should_return_withdrawals_correctly() { @@ -1296,14 +1319,19 @@ public AllowNullAuthorizationTuple(ulong chainId, Address? codeAddress, ulong no } } - private static (byte[] ByteCode, AccessListItemForRpc[] AccessList) GetTestAccessList(long loads = 2, bool allowSystemUser = true) + private static (byte[] ByteCode, AccessListForRpc AccessList) GetTestAccessList(long loads = 2, bool allowSystemUser = true) { - AccessListItemForRpc[] accessList = allowSystemUser - ? new[] { - new AccessListItemForRpc(Address.SystemUser, Enumerable.Range(1, (int)loads).Select(i => (UInt256)i).ToArray()), - new AccessListItemForRpc(TestItem.AddressC, Array.Empty()), + var builder = new AccessList.Builder(); + if (allowSystemUser) + { + builder.AddAddress(Address.SystemUser); + for (int i = 0; i < (int)loads; i++) + { + builder.AddStorage((UInt256)(i + 1)); } - : new[] { new AccessListItemForRpc(TestItem.AddressC, Array.Empty()) }; + } + builder.AddAddress(TestItem.AddressC); + AccessList accessList = builder.Build(); Prepare code = Prepare.EvmCode; @@ -1326,7 +1354,7 @@ private static (byte[] ByteCode, AccessListItemForRpc[] AccessList) GetTestAcces .PushData(0) .Op(Instruction.RETURN) .Done; - return (byteCode, accessList); + return (byteCode, AccessListForRpc.FromAccessList(accessList)); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs index b3d887961c1..50a822efecf 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs @@ -13,9 +13,8 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Crypto; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; -using Nethermind.JsonRpc.Data; using Nethermind.Logging; using Nethermind.Specs; using Nethermind.Specs.Forks; @@ -142,7 +141,7 @@ protected static byte[] GenerateTransactionDataForEcRecover(Hash256 keccak, Sign { SystemTransaction transaction = new() { Data = bytes, To = toAddress, SenderAddress = senderAddress }; transaction.Hash = transaction.CalculateHash(); - TransactionForRpc transactionForRpc = new(transaction); + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(transaction); ResultWrapper mainChainResult = testRpcBlockchain.EthRpcModule.eth_call(transactionForRpc, BlockParameter.Pending); return ParseEcRecoverAddress(Bytes.FromHexString(mainChainResult.Data)); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs index f25ebc9c145..c79fb4f5f9e 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs @@ -13,7 +13,7 @@ using Nethermind.Core.Test.Builders; using Nethermind.Evm; using Nethermind.Evm.Precompiles; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.JsonRpc.Modules.Eth; @@ -37,7 +37,8 @@ public async Task Test_eth_simulate_create() GasPrice = 20.GWei() }; - TransactionForRpc transactionForRpc = new(systemTransactionForModifiedVm) { Nonce = null }; + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(systemTransactionForModifiedVm); + ((LegacyTransactionForRpc)transactionForRpc).Nonce = null; SimulatePayload payload = new() { @@ -143,17 +144,15 @@ function ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public returns( Assert.That(headHash != chain.BlockFinder.Head!.Hash!); chain.State.StateRoot = chain.BlockFinder.Head!.StateRoot!; - TransactionForRpc transactionForRpc = new(new Transaction + TransactionForRpc transactionForRpc = TransactionForRpc.FromTransaction(new Transaction { Data = transactionData, To = contractAddress, SenderAddress = TestItem.AddressA, GasLimit = 3_500_000, GasPrice = 20.GWei() - }) - { - Nonce = null - }; + }); + ((LegacyTransactionForRpc)transactionForRpc).Nonce = null; SimulatePayload payload = new() { diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs index e653e6914c2..bcfa68f448a 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs @@ -12,7 +12,7 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Int256; @@ -59,7 +59,7 @@ public async Task Test_eth_simulate_serialisation() new() { BlockOverrides = new BlockOverride { Number = 10 }, - Calls = [new TransactionForRpc(txToFail), new TransactionForRpc(tx)], + Calls = [TransactionForRpc.FromTransaction(txToFail), TransactionForRpc.FromTransaction(tx)], StateOverrides = new Dictionary { { TestItem.AddressA, new AccountOverride { Balance = Math.Max(420_000_004_000_001UL, 1_000_000_004_000_001UL) } } @@ -117,7 +117,8 @@ public async Task Test_eth_simulate_eth_moved() FeeRecipient = TestItem.AddressC, BaseFeePerGas = 0 }, - Calls = new[] { new TransactionForRpc(txAtoB1), new TransactionForRpc(txAtoB2) } + Calls = [TransactionForRpc.FromTransaction(txAtoB1), TransactionForRpc.FromTransaction(txAtoB2) + ] }, new() { @@ -129,7 +130,7 @@ public async Task Test_eth_simulate_eth_moved() FeeRecipient = TestItem.AddressC, BaseFeePerGas = 0 }, - Calls = new[] { new TransactionForRpc(txAtoB3), new TransactionForRpc(txAtoB4) } + Calls = [TransactionForRpc.FromTransaction(txAtoB3), TransactionForRpc.FromTransaction(txAtoB4)] } }, TraceTransfers = true @@ -180,8 +181,12 @@ public async Task Test_eth_simulate_transactions_forced_fail() //shall fail Transaction txAtoB2 = GetTransferTxData(nonceA + 2, chain.EthereumEcdsa, TestItem.PrivateKeyA, TestItem.AddressB, UInt256.MaxValue); - TransactionForRpc transactionForRpc = new(txAtoB2) { Nonce = null }; - TransactionForRpc transactionForRpc2 = new(txAtoB1) { Nonce = null }; + + LegacyTransactionForRpc transactionForRpc = (LegacyTransactionForRpc)TransactionForRpc.FromTransaction(txAtoB2); + transactionForRpc.Nonce = null; + LegacyTransactionForRpc transactionForRpc2 = (LegacyTransactionForRpc)TransactionForRpc.FromTransaction(txAtoB1); + transactionForRpc2.Nonce = null; + SimulatePayload payload = new() { BlockStateCalls = new List> @@ -196,7 +201,7 @@ public async Task Test_eth_simulate_transactions_forced_fail() FeeRecipient = TestItem.AddressC, BaseFeePerGas = 0 }, - Calls = new[] { transactionForRpc2 } + Calls = [transactionForRpc2] }, new() { diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsHiveBase.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsHiveBase.cs index 16bd45f4f5d..5653e15fffa 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsHiveBase.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsHiveBase.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Nethermind.Blockchain.Find; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Serialization.Json; using NUnit.Framework; diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs index 708dd5575c1..b2603727410 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs @@ -316,7 +316,7 @@ public async Task parity_enode() [Test] public async Task parity_setEngineSigner() { - string serialized = await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_setEngineSigner", TestItem.AddressA.ToString(), "password"); + string serialized = await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_setEngineSigner", TestItem.AddressA, "password"); string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":67}"; Assert.That(serialized, Is.EqualTo(expectedResult)); _signerStore.Address.Should().Be(TestItem.AddressA); @@ -336,7 +336,7 @@ public async Task parity_setEngineSignerSecret() [Test] public async Task parity_clearEngineSigner() { - await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_setEngineSigner", TestItem.AddressA.ToString(), "password"); + await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_setEngineSigner", TestItem.AddressA, "password"); string serialized = await RpcTest.TestSerializedRequest(_parityRpcModule, "parity_clearEngineSigner"); string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":67}"; serialized.Should().Be(expectedResult); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs index 1d5dab0e6b9..1919d4dc2f7 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs @@ -15,7 +15,6 @@ using Nethermind.Evm; using Nethermind.JsonRpc.Modules.Proof; using Nethermind.Logging; -using Nethermind.Serialization.Json; using Nethermind.Serialization.Rlp; using Nethermind.Specs; using Nethermind.Specs.Forks; @@ -28,7 +27,7 @@ using FluentAssertions; using Nethermind.Consensus.Processing; using Nethermind.Core.Buffers; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.State.Tracing; using NSubstitute; @@ -88,7 +87,7 @@ public async Task Setup() public async Task Can_get_transaction(bool withHeader) { Hash256 txHash = _blockTree.FindBlock(1)!.Transactions[0].Hash!; - TransactionWithProof txWithProof = _proofRpcModule.proof_getTransactionByHash(txHash, withHeader).Data; + TransactionForRpcWithProof txWithProof = _proofRpcModule.proof_getTransactionByHash(txHash, withHeader).Data; Assert.That(txWithProof.Transaction, Is.Not.Null); Assert.That(txWithProof.TxProof.Length, Is.EqualTo(2)); if (withHeader) @@ -100,7 +99,7 @@ public async Task Can_get_transaction(bool withHeader) Assert.That(txWithProof.BlockHeader, Is.Null); } - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", txHash, withHeader); Assert.That(response.Contains("\"result\""), Is.True); } @@ -109,7 +108,7 @@ public async Task Can_get_transaction(bool withHeader) public async Task When_getting_non_existing_tx_correct_error_code_is_returned(bool withHeader) { Hash256 txHash = TestItem.KeccakH; - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", txHash, withHeader); Assert.That(response.Contains($"{ErrorCodes.ResourceNotFound}"), Is.True); } @@ -118,7 +117,7 @@ public async Task When_getting_non_existing_tx_correct_error_code_is_returned(bo public async Task When_getting_non_existing_receipt_correct_error_code_is_returned(bool withHeader) { Hash256 txHash = TestItem.KeccakH; - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash, withHeader); Assert.That(response.Contains($"{ErrorCodes.ResourceNotFound}"), Is.True); } @@ -128,23 +127,23 @@ public async Task On_incorrect_params_returns_correct_error_code() Hash256 txHash = TestItem.KeccakH; // missing with header - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "missing"); // too many - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", "true", "false"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash, true, false); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "too many"); // missing with header - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", txHash); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "missing"); // too many - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", $"{txHash}", "true", "false"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionByHash", txHash, true, false); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "too many"); // all wrong - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{txHash}"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", txHash); Assert.That(response.Contains($"{ErrorCodes.InvalidParams}"), Is.True, "missing"); } @@ -166,7 +165,7 @@ public async Task Can_get_receipt(bool withHeader, string expectedResult) Assert.That(receiptWithProof.BlockHeader, Is.Null); } - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash, withHeader); response.Should().Be(expectedResult); } @@ -236,7 +235,7 @@ public async Task Get_receipt_when_block_has_few_receipts(bool withHeader, strin Assert.That(receiptWithProof.BlockHeader, Is.Null); } - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", $"{txHash}", $"{withHeader}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_getTransactionReceipt", txHash, withHeader); response.Should().Be(expectedResult); } @@ -251,7 +250,7 @@ public async Task Can_call() // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { From = TestItem.AddressA, To = TestItem.AddressB, @@ -260,8 +259,7 @@ public async Task Can_call() _proofRpcModule.proof_call(tx, new BlockParameter(block.Number)); - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{block.Number}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, block.Number); Assert.That(response.Contains("\"result\""), Is.True); } @@ -276,7 +274,7 @@ public async Task Can_call_by_hash() // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { From = TestItem.AddressA, To = TestItem.AddressB, @@ -284,8 +282,7 @@ public async Task Can_call_by_hash() }; _proofRpcModule.proof_call(tx, new BlockParameter(block.Hash!)); - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{block.Hash}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, block.Hash); Assert.That(response.Contains("\"result\""), Is.True); } @@ -300,18 +297,17 @@ public async Task Can_call_by_hash_canonical() // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { From = TestItem.AddressA, To = TestItem.AddressB, GasPrice = _useNonZeroGasPrice ? 10.GWei() : 0 }; - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{{\"blockHash\" : \"{block.Hash}\", \"requireCanonical\" : true}}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, new { blockHash = block.Hash, requireCanonical = true }); Assert.That(response.Contains("-32000"), Is.True); - response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{{\"blockHash\" : \"{TestItem.KeccakG}\", \"requireCanonical\" : true}}"); + response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, new { blockHash = TestItem.KeccakG, requireCanonical = true }); Assert.That(response.Contains("-32001"), Is.True); } @@ -780,7 +776,7 @@ private async Task TestCallWithCode(byte[] code, Address? f // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { From = from, To = TestItem.AddressB, @@ -799,8 +795,7 @@ private async Task TestCallWithCode(byte[] code, Address? f } } - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{blockOnTop.Number}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, blockOnTop.Number); Assert.That(response.Contains("\"result\""), Is.True); return callResultWithProof; @@ -827,7 +822,7 @@ private async Task TestCallWithStorageAndCode(byte[] code, UInt256 gasPrice, Add // would need to setup state root somehow... - TransactionForRpc tx = new() + TransactionForRpc tx = new LegacyTransactionForRpc { // we are testing system transaction here when From is null From = from, @@ -874,8 +869,7 @@ private async Task TestCallWithStorageAndCode(byte[] code, UInt256 gasPrice, Add } } - EthereumJsonSerializer serializer = new(); - string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", $"{serializer.Serialize(tx)}", $"{blockOnTop.Number}"); + string response = await RpcTest.TestSerializedRequest(_proofRpcModule, "proof_call", tx, blockOnTop.Number); Assert.That(response.Contains("\"result\""), Is.True); } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListForRpcTests.cs new file mode 100644 index 00000000000..599ff7b0f47 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListForRpcTests.cs @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using FluentAssertions.Json; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Int256; +using Nethermind.Serialization.Json; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public class AccessListForRpcTests +{ + private readonly EthereumJsonSerializer _serializer = new(); + + [Test] + public void Single_address_with_no_storage() + { + Address address = TestItem.AddressA; + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":[]}]"""); + } + + [Test] + public void Single_address_with_multiple_storage_keys() + { + Address address = TestItem.AddressA; + UInt256 storageKey1 = (UInt256)1; + UInt256 storageKey2 = (UInt256)2; + UInt256 storageKey3 = (UInt256)3; + + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey2) + .AddStorage(storageKey3) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000003"]}]"""); + } + + [Test] + public void Single_address_with_duplicated_storage_keys() + { + Address address = TestItem.AddressA; + UInt256 storageKey1 = (UInt256)1; + UInt256 storageKey2 = (UInt256)2; + UInt256 storageKey3 = (UInt256)3; + + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey2) + .AddStorage(storageKey3) + .AddStorage(storageKey1) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000000000000000000000000000000000000001"]}]"""); + } + + [Test] + public void Duplicated_address_with_multiple_storage_keys() + { + Address address = TestItem.AddressA; + UInt256 storageKey1 = (UInt256)1; + UInt256 storageKey2 = (UInt256)2; + UInt256 storageKey3 = (UInt256)3; + + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey2) + .AddAddress(address) + .AddStorage(storageKey3) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002"]},{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000003"]}]"""); + } + + [Test] + public void Duplicated_address_with_duplicated_storage_keys() + { + Address address = TestItem.AddressA; + UInt256 storageKey1 = (UInt256)1; + UInt256 storageKey2 = (UInt256)2; + UInt256 storageKey3 = (UInt256)3; + + AccessList accessList = new AccessList.Builder() + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey2) + .AddAddress(address) + .AddStorage(storageKey1) + .AddStorage(storageKey3) + .Build(); + + AccessListForRpc forRpc = AccessListForRpc.FromAccessList(accessList); + string serialized = _serializer.Serialize(forRpc); + + JToken.Parse(serialized).Should().BeEquivalentTo("""[{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002"]},{"address":"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000003"]}]"""); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListTransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListTransactionForRpcTests.cs new file mode 100644 index 00000000000..d1ad395d10b --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/AccessListTransactionForRpcTests.cs @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class AccessListTransactionForRpcTests +{ + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction.WithType(TxType.AccessList); + public static readonly Transaction[] Transactions = + [ + Build.TestObject, + + Build.WithNonce(UInt256.Zero).TestObject, + Build.WithNonce((UInt256)123).TestObject, + Build.WithNonce(UInt256.MaxValue).TestObject, + + Build.WithTo(null).TestObject, + Build.WithTo(TestItem.AddressA).TestObject, + Build.WithTo(TestItem.AddressB).TestObject, + Build.WithTo(TestItem.AddressC).TestObject, + Build.WithTo(TestItem.AddressD).TestObject, + Build.WithTo(TestItem.AddressE).TestObject, + Build.WithTo(TestItem.AddressF).TestObject, + + Build.WithGasLimit(0).TestObject, + Build.WithGasLimit(123).TestObject, + Build.WithGasLimit(long.MaxValue).TestObject, + + Build.WithValue(UInt256.Zero).TestObject, + Build.WithValue((UInt256)123).TestObject, + Build.WithValue(UInt256.MaxValue).TestObject, + + Build.WithData(TestItem.RandomDataA).TestObject, + Build.WithData(TestItem.RandomDataB).TestObject, + Build.WithData(TestItem.RandomDataC).TestObject, + Build.WithData(TestItem.RandomDataD).TestObject, + + Build.WithGasPrice(UInt256.Zero).TestObject, + Build.WithGasPrice((UInt256)123).TestObject, + Build.WithGasPrice(UInt256.MaxValue).TestObject, + + Build.WithChainId(null).TestObject, + Build.WithChainId(BlockchainIds.Mainnet).TestObject, + Build.WithChainId(BlockchainIds.Sepolia).TestObject, + Build.WithChainId(0).TestObject, + Build.WithChainId(ulong.MaxValue).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddAddress(TestItem.AddressB) + .AddStorage(3) + .Build()).TestObject, + + Build.WithSignature(TestItem.RandomSignatureA).TestObject, + Build.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x1$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("gasPrice").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + // Suprising inconsistency in `FluentAssertions` where `AllSatisfy` fails on empty collections. + // This requires wrapping the assertion in a condition. + // See: https://github.com/fluentassertions/fluentassertions/discussions/2143#discussioncomment-9677309 + var accessList = json.GetProperty("accessList").EnumerateArray(); + if (accessList.Any()) + { + accessList.Should().AllSatisfy(item => + { + item.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + item.GetProperty("storageKeys").EnumerateArray().Should().AllSatisfy(key => + key.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + }); + } + json.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var yParity = json.GetProperty("yParity").GetString(); + yParity.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + if (json.TryGetProperty("v", out var v)) + { + v.GetString().Should().Be(yParity); + } + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/BlobTransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/BlobTransactionForRpcTests.cs new file mode 100644 index 00000000000..add25e10ad9 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/BlobTransactionForRpcTests.cs @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class BlobTransactionForRpcTests +{ + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction + .WithType(TxType.Blob) + // NOTE: We require to initialize these properties to non-null values + .WithMaxFeePerBlobGas(UInt256.Zero) + .WithBlobVersionedHashes([]); + + public static readonly Transaction[] Transactions = + [ + Build.TestObject, + + Build.WithNonce(UInt256.Zero).TestObject, + Build.WithNonce(123).TestObject, + Build.WithNonce(UInt256.MaxValue).TestObject, + + Build.WithTo(null).TestObject, + Build.WithTo(TestItem.AddressA).TestObject, + Build.WithTo(TestItem.AddressB).TestObject, + Build.WithTo(TestItem.AddressC).TestObject, + Build.WithTo(TestItem.AddressD).TestObject, + Build.WithTo(TestItem.AddressE).TestObject, + Build.WithTo(TestItem.AddressF).TestObject, + + Build.WithGasLimit(0).TestObject, + Build.WithGasLimit(123).TestObject, + Build.WithGasLimit(long.MaxValue).TestObject, + + Build.WithValue(UInt256.Zero).TestObject, + Build.WithValue(123).TestObject, + Build.WithValue(UInt256.MaxValue).TestObject, + + Build.WithData(TestItem.RandomDataA).TestObject, + Build.WithData(TestItem.RandomDataB).TestObject, + Build.WithData(TestItem.RandomDataC).TestObject, + Build.WithData(TestItem.RandomDataD).TestObject, + + Build.WithMaxPriorityFeePerGas(UInt256.Zero).TestObject, + Build.WithMaxPriorityFeePerGas(123).TestObject, + Build.WithMaxPriorityFeePerGas(UInt256.MaxValue).TestObject, + + Build.WithMaxFeePerGas(UInt256.Zero).TestObject, + Build.WithMaxFeePerGas(123).TestObject, + Build.WithMaxFeePerGas(UInt256.MaxValue).TestObject, + + Build.WithMaxFeePerBlobGas(UInt256.Zero).TestObject, + Build.WithMaxFeePerBlobGas(123).TestObject, + Build.WithMaxFeePerBlobGas(UInt256.MaxValue).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddAddress(TestItem.AddressB) + .AddStorage(3) + .Build()).TestObject, + + Build.WithBlobVersionedHashes(0).TestObject, + Build.WithBlobVersionedHashes(1).TestObject, + Build.WithBlobVersionedHashes(50).TestObject, + + Build.WithChainId(null).TestObject, + Build.WithChainId(BlockchainIds.Mainnet).TestObject, + Build.WithChainId(BlockchainIds.Sepolia).TestObject, + Build.WithChainId(0).TestObject, + Build.WithChainId(ulong.MaxValue).TestObject, + + Build.WithSignature(TestItem.RandomSignatureA).TestObject, + Build.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x3$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("maxPriorityFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("maxFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("maxFeePerBlobGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var accessList = json.GetProperty("accessList").EnumerateArray(); + if (accessList.Any()) + { + accessList.Should().AllSatisfy(item => + { + item.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + item.GetProperty("storageKeys").EnumerateArray().Should().AllSatisfy(key => + key.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + }); + } + var blobVersionedHashes = json.GetProperty("blobVersionedHashes").EnumerateArray(); + if (blobVersionedHashes.Any()) + { + blobVersionedHashes.Should().AllSatisfy(hash => + hash.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + } + json.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("yParity").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + + // Assert deprecated fields are no longer serialized + json.TryGetProperty("v", out _).Should().BeFalse(); + json.TryGetProperty("gasPrice", out _).Should().BeFalse(); + + // Assert deserialization-only are not serialized + json.TryGetProperty("blobs", out _).Should().BeFalse(); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/EIP1559TransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/EIP1559TransactionForRpcTests.cs new file mode 100644 index 00000000000..13beb3b643b --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/EIP1559TransactionForRpcTests.cs @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class EIP1559TransactionForRpcTests +{ + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction.WithType(TxType.EIP1559); + public static readonly Transaction[] Transactions = + [ + Build.TestObject, + + Build.WithNonce(UInt256.Zero).TestObject, + Build.WithNonce(123).TestObject, + Build.WithNonce(UInt256.MaxValue).TestObject, + + Build.WithTo(null).TestObject, + Build.WithTo(TestItem.AddressA).TestObject, + Build.WithTo(TestItem.AddressB).TestObject, + Build.WithTo(TestItem.AddressC).TestObject, + Build.WithTo(TestItem.AddressD).TestObject, + Build.WithTo(TestItem.AddressE).TestObject, + Build.WithTo(TestItem.AddressF).TestObject, + + Build.WithGasLimit(0).TestObject, + Build.WithGasLimit(123).TestObject, + Build.WithGasLimit(long.MaxValue).TestObject, + + Build.WithValue(UInt256.Zero).TestObject, + Build.WithValue(123).TestObject, + Build.WithValue(UInt256.MaxValue).TestObject, + + Build.WithData(TestItem.RandomDataA).TestObject, + Build.WithData(TestItem.RandomDataB).TestObject, + Build.WithData(TestItem.RandomDataC).TestObject, + Build.WithData(TestItem.RandomDataD).TestObject, + + Build.WithMaxPriorityFeePerGas(UInt256.Zero).TestObject, + Build.WithMaxPriorityFeePerGas(123).TestObject, + Build.WithMaxPriorityFeePerGas(UInt256.MaxValue).TestObject, + + Build.WithMaxFeePerGas(UInt256.Zero).TestObject, + Build.WithMaxFeePerGas(123).TestObject, + Build.WithMaxFeePerGas(UInt256.MaxValue).TestObject, + + Build.WithGasPrice(UInt256.Zero).TestObject, + Build.WithGasPrice(123).TestObject, + Build.WithGasPrice(UInt256.MaxValue).TestObject, + + Build.WithChainId(null).TestObject, + Build.WithChainId(BlockchainIds.Mainnet).TestObject, + Build.WithChainId(BlockchainIds.Sepolia).TestObject, + Build.WithChainId(0).TestObject, + Build.WithChainId(ulong.MaxValue).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddAddress(TestItem.AddressB) + .AddStorage(3) + .Build()).TestObject, + + Build.WithSignature(TestItem.RandomSignatureA).TestObject, + Build.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x2$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("maxPriorityFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("maxFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("gasPrice").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var accessList = json.GetProperty("accessList").EnumerateArray(); + if (accessList.Any()) + { + accessList.Should().AllSatisfy(item => + { + item.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + item.GetProperty("storageKeys").EnumerateArray().Should().AllSatisfy(key => + key.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + }); + } + json.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var yParity = json.GetProperty("yParity").GetString(); + yParity.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + if (json.TryGetProperty("v", out var v)) + { + v.GetString().Should().Be(yParity); + } + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/LegacyTransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/LegacyTransactionForRpcTests.cs new file mode 100644 index 00000000000..3173927512c --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/LegacyTransactionForRpcTests.cs @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class LegacyTransactionForRpcTests +{ + private static TransactionBuilder BuildALegacyTransaction => Build.A.Transaction.WithType(TxType.Legacy); + public static readonly Transaction[] Transactions = + [ + BuildALegacyTransaction.TestObject, + + BuildALegacyTransaction.WithNonce(UInt256.Zero).TestObject, + BuildALegacyTransaction.WithNonce((UInt256)123).TestObject, + BuildALegacyTransaction.WithNonce(UInt256.MaxValue).TestObject, + + BuildALegacyTransaction.WithTo(null).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressA).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressB).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressC).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressD).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressE).TestObject, + BuildALegacyTransaction.WithTo(TestItem.AddressF).TestObject, + + BuildALegacyTransaction.WithGasLimit(0).TestObject, + BuildALegacyTransaction.WithGasLimit(123).TestObject, + BuildALegacyTransaction.WithGasLimit(long.MaxValue).TestObject, + + BuildALegacyTransaction.WithValue(UInt256.Zero).TestObject, + BuildALegacyTransaction.WithValue((UInt256)123).TestObject, + BuildALegacyTransaction.WithValue(UInt256.MaxValue).TestObject, + + BuildALegacyTransaction.WithData(TestItem.RandomDataA).TestObject, + BuildALegacyTransaction.WithData(TestItem.RandomDataB).TestObject, + BuildALegacyTransaction.WithData(TestItem.RandomDataC).TestObject, + BuildALegacyTransaction.WithData(TestItem.RandomDataD).TestObject, + + BuildALegacyTransaction.WithGasPrice(UInt256.Zero).TestObject, + BuildALegacyTransaction.WithGasPrice((UInt256)123).TestObject, + BuildALegacyTransaction.WithGasPrice(UInt256.MaxValue).TestObject, + + BuildALegacyTransaction.WithChainId(null).TestObject, + BuildALegacyTransaction.WithChainId(BlockchainIds.Mainnet).TestObject, + BuildALegacyTransaction.WithChainId(BlockchainIds.Sepolia).TestObject, + BuildALegacyTransaction.WithChainId(0).TestObject, + BuildALegacyTransaction.WithChainId(ulong.MaxValue).TestObject, + + BuildALegacyTransaction.WithSignature(TestItem.RandomSignatureA).TestObject, + BuildALegacyTransaction.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x0$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("from").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("gasPrice").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + bool hasChainId = json.TryGetProperty("chainId", out var chainId); + if (hasChainId) + { + chainId.GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } + json.GetProperty("v").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/SetCodeTransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/SetCodeTransactionForRpcTests.cs new file mode 100644 index 00000000000..4f951f73f0a --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/SetCodeTransactionForRpcTests.cs @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public static class SetCodeTransactionForRpcTests +{ + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction.WithType(TxType.SetCode) + // NOTE: We require to initialize the authorization list + .WithAuthorizationCodeIfAuthorizationListTx(); + + public static readonly Transaction[] Transactions = + [ + Build.TestObject, + + Build.WithNonce(UInt256.Zero).TestObject, + Build.WithNonce((UInt256)123).TestObject, + Build.WithNonce(UInt256.MaxValue).TestObject, + + Build.WithTo(null).TestObject, + Build.WithTo(TestItem.AddressA).TestObject, + Build.WithTo(TestItem.AddressB).TestObject, + Build.WithTo(TestItem.AddressC).TestObject, + Build.WithTo(TestItem.AddressD).TestObject, + Build.WithTo(TestItem.AddressE).TestObject, + Build.WithTo(TestItem.AddressF).TestObject, + + Build.WithGasLimit(0).TestObject, + Build.WithGasLimit(123).TestObject, + Build.WithGasLimit(long.MaxValue).TestObject, + + Build.WithValue(UInt256.Zero).TestObject, + Build.WithValue((UInt256)123).TestObject, + Build.WithValue(UInt256.MaxValue).TestObject, + + Build.WithData(TestItem.RandomDataA).TestObject, + Build.WithData(TestItem.RandomDataB).TestObject, + Build.WithData(TestItem.RandomDataC).TestObject, + Build.WithData(TestItem.RandomDataD).TestObject, + + Build.WithGasPrice(UInt256.Zero).TestObject, + Build.WithGasPrice((UInt256)123).TestObject, + Build.WithGasPrice(UInt256.MaxValue).TestObject, + + Build.WithChainId(null).TestObject, + Build.WithChainId(BlockchainIds.Mainnet).TestObject, + Build.WithChainId(BlockchainIds.Sepolia).TestObject, + Build.WithChainId(0).TestObject, + Build.WithChainId(ulong.MaxValue).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAccessList(new AccessList.Builder() + .AddAddress(TestItem.AddressA) + .AddStorage(1) + .AddStorage(2) + .AddAddress(TestItem.AddressB) + .AddStorage(3) + .Build()).TestObject, + + Build.WithAuthorizationCode(new AuthorizationTuple( + chainId: BlockchainIds.Mainnet, + codeAddress: TestItem.AddressA, + nonce: 123, + sig: TestItem.RandomSignatureA + )).TestObject, + + Build.WithAuthorizationCode(new AuthorizationTuple( + chainId: BlockchainIds.Sepolia, + codeAddress: TestItem.AddressA, + nonce: ulong.MaxValue, + sig: TestItem.RandomSignatureA + )).WithAuthorizationCode(new AuthorizationTuple( + chainId: BlockchainIds.Sepolia, + codeAddress: TestItem.AddressB, + nonce: 0, + sig: TestItem.RandomSignatureB + )).TestObject, + + Build.WithSignature(TestItem.RandomSignatureA).TestObject, + Build.WithSignature(TestItem.RandomSignatureB).TestObject, + ]; + + public static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x4$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("maxPriorityFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("maxFeePerGas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var accessList = json.GetProperty("accessList").EnumerateArray(); + if (accessList.Any()) + { + accessList.Should().AllSatisfy(item => + { + item.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + item.GetProperty("storageKeys").EnumerateArray().Should().AllSatisfy(key => + key.GetString().Should().MatchRegex("^0x[0-9a-f]{64}$") + ); + }); + } + json.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + // NOTE: Empty authorization lists are considered invalid + json.GetProperty("authorizationList").EnumerateArray().Should().AllSatisfy(tuple => + { + tuple.GetProperty("chainId").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + tuple.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + tuple.GetProperty("address").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + tuple.GetProperty("yParity").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + tuple.GetProperty("r").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + tuple.GetProperty("s").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + }); + + // Assert deprecated fields are no longer serialized + json.TryGetProperty("gasPrice", out _).Should().BeFalse(); + json.TryGetProperty("yParity", out _).Should().BeFalse(); + json.TryGetProperty("v", out _).Should().BeFalse(); + json.TryGetProperty("r", out _).Should().BeFalse(); + json.TryGetProperty("s", out _).Should().BeFalse(); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/TransactionForRpcTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/TransactionForRpcTests.cs new file mode 100644 index 00000000000..23e9aeb10b2 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcTransaction/TransactionForRpcTests.cs @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Json; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Serialization.Json; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.Test.Modules.RpcTransaction; + +public class TransactionForRpcTests +{ + private readonly IJsonSerializer _serializer = new EthereumJsonSerializer(); + + public static readonly ulong SomeChainId = 123ul; + + public static readonly Transaction[] Transactions = + [ + .. LegacyTransactionForRpcTests.Transactions, + .. AccessListTransactionForRpcTests.Transactions, + .. EIP1559TransactionForRpcTests.Transactions, + .. BlobTransactionForRpcTests.Transactions, + .. SetCodeTransactionForRpcTests.Transactions, + ]; + + [Test] + public void R_and_s_are_quantity_and_not_data() + { + byte[] r = new byte[32]; + byte[] s = new byte[32]; + r[1] = 1; + s[2] = 2; + + Transaction tx = new() + { + Signature = new Signature(r, s, 27) + }; + + var txForRpc = TransactionForRpc.FromTransaction(tx); + + EthereumJsonSerializer serializer = new(); + string serialized = serializer.Serialize(txForRpc); + + var json = JObject.Parse(serialized); + var expectedS = JObject.Parse("""{ "s": "0x20000000000000000000000000000000000000000000000000000000000"}"""); + var expectedR = JObject.Parse("""{ "r": "0x1000000000000000000000000000000000000000000000000000000000000"}"""); + + json.Should().ContainSubtree(expectedS); + json.Should().ContainSubtree(expectedR); + } + + [TestCaseSource(nameof(Transactions))] + public void Serialized_JSON_satisfies_schema(Transaction transaction) + { + TransactionForRpc rpcTransaction = TransactionForRpc.FromTransaction(transaction, chainId: SomeChainId); + string serialized = _serializer.Serialize(rpcTransaction); + using var jsonDocument = JsonDocument.Parse(serialized); + JsonElement json = jsonDocument.RootElement; + + switch (transaction.Type) + { + case TxType.Legacy: + LegacyTransactionForRpcTests.ValidateSchema(json); + break; + case TxType.AccessList: + AccessListTransactionForRpcTests.ValidateSchema(json); + break; + case TxType.EIP1559: + EIP1559TransactionForRpcTests.ValidateSchema(json); + break; + case TxType.Blob: + BlobTransactionForRpcTests.ValidateSchema(json); + break; + case TxType.SetCode: + SetCodeTransactionForRpcTests.ValidateSchema(json); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + [TestCaseSource(nameof(Transactions))] + public void Serialized_JSON_satisfies_Nethermind_fields_schema(Transaction transaction) + { + TransactionForRpc rpcTransaction = TransactionForRpc.FromTransaction(transaction, chainId: SomeChainId); + string serialized = _serializer.Serialize(rpcTransaction); + using var jsonDocument = JsonDocument.Parse(serialized); + JsonElement json = jsonDocument.RootElement; + + json.GetProperty("hash").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{64}$"); + json.GetProperty("transactionIndex").GetString()?.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("blockHash").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{64}$"); + json.GetProperty("blockNumber").GetString()?.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs index 1d132d4bf33..9abd1ff32e3 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using FluentAssertions.Json; using Nethermind.Blockchain; using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.Find; @@ -29,7 +30,7 @@ using Nethermind.Specs; using Nethermind.Synchronization.ParallelSync; using Nethermind.TxPool; - +using Newtonsoft.Json.Linq; using NSubstitute; using NUnit.Framework; @@ -93,6 +94,8 @@ public void Setup() BlockHeader toBlock = Build.A.BlockHeader.WithNumber(77777).TestObject; _blockTree.FindHeader(Arg.Any()).Returns(fromBlock); _blockTree.FindHeader(Arg.Any(), true).Returns(toBlock); + + _specProvider.ChainId.Returns((ulong)BlockchainIds.Mainnet); } [TearDown] @@ -145,7 +148,7 @@ private List GetLogsSubscriptionResult(Filter filter, BlockReplac private JsonRpcResult GetNewPendingTransactionsResult(TxEventArgs txEventArgs, out string subscriptionId, TransactionsOption? option = null) { - NewPendingTransactionsSubscription newPendingTransactionsSubscription = new(_jsonRpcDuplexClient, _txPool, _logManager, option); + NewPendingTransactionsSubscription newPendingTransactionsSubscription = new(_jsonRpcDuplexClient, _txPool, _specProvider, _logManager, option); JsonRpcResult jsonRpcResult = new(); ManualResetEvent manualResetEvent = new(false); @@ -816,8 +819,8 @@ public void NewPendingTransactionsSubscription_on_NewPending_with_includeTransac jsonRpcResult.Response.Should().NotBeNull(); string serialized = _jsonSerializer.Serialize(jsonRpcResult.Response); - var expectedResult = string.Concat("{\"jsonrpc\":\"2.0\",\"method\":\"eth_subscription\",\"params\":{\"subscription\":\"", subscriptionId, "\",\"result\":{\"nonce\":\"0x0\",\"blockHash\":null,\"blockNumber\":null,\"transactionIndex\":null,\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\",\"gasPrice\":\"0x1\",\"gas\":\"0x5208\",\"input\":\"0x\",\"type\":\"0x0\"}}}"); - expectedResult.Should().Be(serialized); + + JToken.Parse(serialized).Should().BeEquivalentTo($$$$"""{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"{{{{subscriptionId}}}}","result":{"nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"to":"0x0000000000000000000000000000000000000000","value":"0x1","gasPrice":"0x1","gas":"0x5208","input":"0x","type":"0x0","hash":null,"v":"0x0","r":"0x0","s":"0x0","from":null}}}"""); } [TestCase(2)] @@ -844,6 +847,7 @@ public async Task NewPendingTransactionSubscription_multiple_fast_messages(int m using NewPendingTransactionsSubscription subscription = new( jsonRpcDuplexClient: client, txPool: txPool, + specProvider: _specProvider, logManager: LimboLogs.Instance); for (int i = 0; i < messages; i++) @@ -882,6 +886,7 @@ public async Task MultipleSubscriptions_concurrent_fast_messages(int messages) // ReSharper disable once AccessToDisposedClosure jsonRpcDuplexClient: client, txPool: txPool, + specProvider: _specProvider, logManager: LimboLogs.Instance); for (int i = 0; i < messages; i++) diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs index ea070b4502f..1a63fa846cd 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs @@ -15,7 +15,6 @@ using Nethermind.Core.Test.Builders; using Nethermind.Crypto; using Nethermind.Facade; -using Nethermind.JsonRpc.Modules; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.Logging; using Nethermind.Facade.Eth; @@ -174,10 +173,7 @@ protected override async Task Build( return this; } - public Task TestEthRpc(string method, params string[] parameters) => + public Task TestEthRpc(string method, params object?[]? parameters) => RpcTest.TestSerializedRequest(EthRpcModule, method, parameters); - - public Task TestSerializedRequest(T module, string method, params string[] parameters) where T : class, IRpcModule => - RpcTest.TestSerializedRequest(module, method, parameters); } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs index 6b264462616..5847c662c46 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs @@ -21,10 +21,9 @@ using Nethermind.Consensus.Tracing; using Nethermind.Consensus.Validators; using Nethermind.Core.Crypto; -using Nethermind.Db; using Nethermind.Evm; using Nethermind.Evm.TransactionProcessing; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Serialization.Json; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; @@ -666,7 +665,7 @@ public async Task Trace_call_without_blockParameter_provided_test() .WithGasLimit(93548).TestObject; await blockchain.AddBlock(transaction2); - TransactionForRpc transactionRpc = new(transaction2); + TransactionForRpc transactionRpc = TransactionForRpc.FromTransaction(transaction2); string[] traceTypes = { "trace" }; @@ -681,8 +680,8 @@ public async Task Trace_call_simple_tx_test() { Context context = new(); await context.Build(); - string transaction = "{\"from\":\"0xaaaaaaaa8583de65cc752fe3fad5098643244d22\",\"to\":\"0xd6a8d04cb9846759416457e2c593c99390092df6\"}"; - string traceTypes = "[\"trace\"]"; + object transaction = new { from = "0xaaaaaaaa8583de65cc752fe3fad5098643244d22", to = "0xd6a8d04cb9846759416457e2c593c99390092df6" }; + string[] traceTypes = { "trace" }; string blockParameter = "latest"; string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0xaaaaaaaa8583de65cc752fe3fad5098643244d22\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xd6a8d04cb9846759416457e2c593c99390092df6\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}"; @@ -693,17 +692,20 @@ public async Task Trace_call_simple_tx_test() Assert.That(serialized, Is.EqualTo(expectedResult), serialized.Replace("\"", "\\\"")); } - [TestCase("{\"from\":\"0x7f554713be84160fdf0178cc8df86f5aabd33397\",\"to\":\"0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f\",\"value\":\"0x0\",\"gasPrice\":\"0x119e04a40a\"}", "[\"trace\"]", "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x7f554713be84160fdf0178cc8df86f5aabd33397\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}")] - [TestCase("{\"from\":\"0xc71acc7863f3bc7347b24c3b835643bd89d4d161\",\"to\":\"0xa760e26aa76747020171fcf8bda108dfde8eb930\",\"value\":\"0x0\",\"gasPrice\":\"0x2108eea5bc\"}", "[\"trace\"]", "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0xc71acc7863f3bc7347b24c3b835643bd89d4d161\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xa760e26aa76747020171fcf8bda108dfde8eb930\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}")] - public async Task Trace_call_without_blockParameter_test(string transaction, string traceTypes, string expectedResult) + private static readonly IEnumerable<(object, string[], string)> Trace_call_without_blockParameter_test_cases = [ + (new { from = "0x7f554713be84160fdf0178cc8df86f5aabd33397", to = "0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f", value = "0x0", gasPrice = "0x119e04a40a" }, ["trace"], "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x7f554713be84160fdf0178cc8df86f5aabd33397\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}"), + (new { from = "0xc71acc7863f3bc7347b24c3b835643bd89d4d161", to = "0xa760e26aa76747020171fcf8bda108dfde8eb930", value = "0x0", gasPrice = "0x2108eea5bc" }, ["trace"], "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0xc71acc7863f3bc7347b24c3b835643bd89d4d161\",\"gas\":\"0x5f58ef8\",\"input\":\"0x\",\"to\":\"0xa760e26aa76747020171fcf8bda108dfde8eb930\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}") + ]; + [TestCaseSource(nameof(Trace_call_without_blockParameter_test_cases))] + public async Task Trace_call_without_blockParameter_test((object transaction, string[] traceTypes, string expectedResult) testCase) { Context context = new(); await context.Build(); string serialized = await RpcTest.TestSerializedRequest( context.TraceRpcModule, - "trace_call", transaction, traceTypes); + "trace_call", testCase.transaction, testCase.traceTypes); - Assert.That(serialized, Is.EqualTo(expectedResult), serialized.Replace("\"", "\\\"")); + Assert.That(serialized, Is.EqualTo(testCase.expectedResult), serialized.Replace("\"", "\\\"")); } [Test] @@ -717,14 +719,14 @@ public async Task Trace_callMany_internal_transactions_test() Transaction transaction1 = Build.A.Transaction.WithNonce(currentNonceAddressA++).WithTo(TestItem.AddressC) .SignedAndResolved(TestItem.PrivateKeyA).TestObject; - TransactionForRpc txForRpc1 = new(transaction1); + TransactionForRpc txForRpc1 = TransactionForRpc.FromTransaction(transaction1); string[] traceTypes1 = { "Trace" }; Transaction transaction2 = Build.A.Transaction.WithNonce(currentNonceAddressA++).WithTo(TestItem.AddressD) .SignedAndResolved(TestItem.PrivateKeyA).TestObject; await blockchain.AddBlock(transaction1, transaction2); - TransactionForRpc txForRpc2 = new(transaction2); + TransactionForRpc txForRpc2 = TransactionForRpc.FromTransaction(transaction2); string[] traceTypes2 = { "Trace" }; BlockParameter numberOrTag = new(16); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TransactionForRpcConverterTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TransactionForRpcConverterTests.cs deleted file mode 100644 index 6b6add97578..00000000000 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TransactionForRpcConverterTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using FluentAssertions; -using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; -using Nethermind.JsonRpc.Test.Data; -using Nethermind.Serialization.Json; -using NUnit.Framework; - -namespace Nethermind.JsonRpc.Test.Modules -{ - [Parallelizable(ParallelScope.Self)] - [TestFixture] - public class TransactionForRpcConverterTests : SerializationTestBase - { - [Test] - public void R_and_s_are_quantity_and_not_data() - { - byte[] r = new byte[32]; - byte[] s = new byte[32]; - r[1] = 1; - s[2] = 2; - - Transaction tx = new(); - tx.Signature = new Signature(r, s, 27); - - TransactionForRpc txForRpc = new(tx); - - EthereumJsonSerializer serializer = new(); - string serialized = serializer.Serialize(txForRpc); - - serialized.Should().Contain("0x20000000000000000000000000000000000000000000000000000000000"); - serialized.Should().Contain("0x1000000000000000000000000000000000000000000000000000000000000"); - Console.WriteLine(serialized); - } - } -} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs new file mode 100644 index 00000000000..be91f5763a0 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TxPoolRpcModuleTests.cs @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +// using System.Collections.Generic; + +using System.Collections.Generic; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.JsonRpc.Modules.TxPool; +using Nethermind.TxPool; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.Test.Modules; + +public class TxPoolRpcModuleTests +{ + [Test] + public void Pool_content_produces_transactions_with_ChainId() + { + const ulong SomeChainId = 123ul; + var txA = Build.A.Transaction + .WithType(TxType.Legacy) + .WithChainId(null) + .TestObject; + var txB = Build.A.Transaction + .WithType(TxType.AccessList) + .WithAccessList(AccessList.Empty) + .WithChainId(null) + .TestObject; + + var txPoolInfoProvider = Substitute.For(); + txPoolInfoProvider.GetInfo().Returns(new TxPoolInfo( + pending: new() + { + { + new AddressAsKey(TestItem.AddressA), new Dictionary + { + { 1, txA } + } + } + }, + queued: new() + { + { + new AddressAsKey(TestItem.AddressB), new Dictionary + { + { 2, txB } + } + } + } + )); + + var specProvider = Substitute.For(); + specProvider.ChainId.Returns(SomeChainId); + + var txPoolRpcModule = new TxPoolRpcModule(txPoolInfoProvider, specProvider); + + var txpoolContent = txPoolRpcModule.txpool_content().Data; + + var rpcTxA = txpoolContent.Pending[new AddressAsKey(TestItem.AddressA)][1] as LegacyTransactionForRpc; + var rpcTxB = txpoolContent.Queued[new AddressAsKey(TestItem.AddressB)][2] as AccessListTransactionForRpc; + + rpcTxA!.ChainId.Should().BeNull(); + rpcTxB!.ChainId.Should().Be(SomeChainId); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/RpcTest.cs b/src/Nethermind/Nethermind.JsonRpc.Test/RpcTest.cs index 4bc26918014..5a235eb5bb2 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/RpcTest.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/RpcTest.cs @@ -1,9 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using System.IO; -using System.Linq; using System.Text.Json; using System.Threading.Tasks; @@ -14,40 +12,38 @@ using Nethermind.Logging; using Nethermind.Serialization.Json; -using NUnit.Framework; - namespace Nethermind.JsonRpc.Test; public static class RpcTest { - public static async Task TestRequest(T module, string method, params string[] parameters) where T : class, IRpcModule + public static async Task TestRequest(T module, string method, params object?[]? parameters) where T : class, IRpcModule { IJsonRpcService service = BuildRpcService(module); - JsonRpcRequest request = GetJsonRequest(method, parameters); + JsonRpcRequest request = BuildJsonRequest(method, parameters); return await service.SendRequestAsync(request, new JsonRpcContext(RpcEndpoint.Http)); } - public static async Task TestSerializedRequest(T module, string method, params string[] parameters) where T : class, IRpcModule + public static async Task TestSerializedRequest(T module, string method, params object?[]? parameters) where T : class, IRpcModule { IJsonRpcService service = BuildRpcService(module); - JsonRpcRequest request = GetJsonRequest(method, parameters); + JsonRpcRequest request = BuildJsonRequest(method, parameters); - JsonRpcContext context = module is IContextAwareRpcModule { Context: not null } contextAwareModule ? - contextAwareModule.Context : - new JsonRpcContext(RpcEndpoint.Http); - using JsonRpcResponse response = await service.SendRequestAsync(request, context); + JsonRpcContext context = module is IContextAwareRpcModule { Context: not null } contextAwareModule + ? contextAwareModule.Context + : new JsonRpcContext(RpcEndpoint.Http); + using JsonRpcResponse response = await service.SendRequestAsync(request, context).ConfigureAwait(false); EthereumJsonSerializer serializer = new(); Stream stream = new MemoryStream(); - long size = await serializer.SerializeAsync(stream, response); + long size = await serializer.SerializeAsync(stream, response).ConfigureAwait(false); // for coverage (and to prove that it does not throw Stream indentedStream = new MemoryStream(); - await serializer.SerializeAsync(indentedStream, response, true); + await serializer.SerializeAsync(indentedStream, response, true).ConfigureAwait(false); stream.Seek(0, SeekOrigin.Begin); - string serialized = await new StreamReader(stream).ReadToEndAsync(); + string serialized = await new StreamReader(stream).ReadToEndAsync().ConfigureAwait(false); size.Should().Be(serialized.Length); @@ -63,18 +59,21 @@ private static IJsonRpcService BuildRpcService(T module) where T : class, IRp return service; } - public static JsonRpcRequest GetJsonRequest(string method, params string[]? parameters) + public static JsonRpcRequest BuildJsonRequest(string method, params object?[]? parameters) { - var doc = JsonDocument.Parse(JsonSerializer.Serialize(parameters?.ToArray())); - var request = new JsonRpcRequest() + // TODO: Eventually we would like to support injecting a custom serializer + var serializer = new EthereumJsonSerializer(); + parameters ??= []; + + var jsonParameters = serializer.Deserialize(serializer.Serialize(parameters)); + + return new JsonRpcRequest { JsonRpc = "2.0", Method = method, - Params = doc.RootElement, + Params = jsonParameters, Id = 67 }; - - return request; } private class TestSingletonFactory(T module) : SingletonFactory(module) diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Test/TraceStoreRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Test/TraceStoreRpcModuleTests.cs index 8c82d299e41..05ec44a9e1a 100644 --- a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Test/TraceStoreRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Test/TraceStoreRpcModuleTests.cs @@ -12,7 +12,7 @@ using Nethermind.Core.Test.Builders; using Nethermind.Db; using Nethermind.Evm.Tracing.ParityStyle; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules.Trace; using Nethermind.Logging; @@ -29,7 +29,10 @@ public void trace_call_returns_from_inner_module() { TestContext test = new(); - test.Module.trace_call(new TransactionForRpc(Build.A.Transaction.TestObject), new[] { ParityTraceTypes.Trace.ToString() }, BlockParameter.Latest) + test.Module.trace_call( + call: TransactionForRpc.FromTransaction(Build.A.Transaction.TestObject), + traceTypes: [ParityTraceTypes.Trace.ToString()], + blockParameter: BlockParameter.Latest) .Should().BeEquivalentTo(ResultWrapper.Success(new ParityTxTraceFromReplay(test.NonDbTraces[0]))); } @@ -38,7 +41,9 @@ public void trace_callMany_returns_from_inner_module() { TestContext test = new(); - TransactionForRpcWithTraceTypes[] calls = { new() { TraceTypes = new[] { ParityTraceTypes.Trace.ToString() }, Transaction = new TransactionForRpc(Build.A.Transaction.TestObject) } }; + TransactionForRpcWithTraceTypes[] calls = [ + new() { TraceTypes = [ParityTraceTypes.Trace.ToString()], Transaction = TransactionForRpc.FromTransaction(Build.A.Transaction.TestObject) } + ]; test.Module.trace_callMany(calls, BlockParameter.Latest) .Should().BeEquivalentTo(ResultWrapper>.Success(test.NonDbTraces.Select(t => new ParityTxTraceFromReplay(t)))); } diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs index d9267e28112..12b38afe90a 100644 --- a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs @@ -7,7 +7,7 @@ using Nethermind.Core.Crypto; using Nethermind.Db; using Nethermind.Evm.Tracing.ParityStyle; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules; using Nethermind.JsonRpc.Modules.Trace; diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/AccessListForRpc.cs b/src/Nethermind/Nethermind.JsonRpc/Data/AccessListForRpc.cs deleted file mode 100644 index 7c21797de61..00000000000 --- a/src/Nethermind/Nethermind.JsonRpc/Data/AccessListForRpc.cs +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; -using Nethermind.Facade.Eth; -using Nethermind.Int256; - -namespace Nethermind.JsonRpc.Data -{ - public readonly struct AccessListForRpc - { - public AccessListForRpc(IEnumerable accessList, in UInt256 gasUsed) - { - AccessList = accessList; - GasUsed = gasUsed; - } - - public IEnumerable AccessList { get; } - - public UInt256 GasUsed { get; } - } -} diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/AccessListResultForRpc.cs b/src/Nethermind/Nethermind.JsonRpc/Data/AccessListResultForRpc.cs new file mode 100644 index 00000000000..a86aa7317b5 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc/Data/AccessListResultForRpc.cs @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Int256; + +namespace Nethermind.JsonRpc.Data; + +public readonly struct AccessListResultForRpc +{ + public AccessListForRpc AccessList { get; init; } + + public UInt256 GasUsed { get; init; } + + public AccessListResultForRpc(AccessListForRpc accessList, in UInt256 gasUsed) + { + AccessList = accessList; + GasUsed = gasUsed; + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpcWithTraceTypes.cs b/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpcWithTraceTypes.cs index 5f5c98acaf1..9ba096aaa6b 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpcWithTraceTypes.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpcWithTraceTypes.cs @@ -4,24 +4,17 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; -namespace Nethermind.JsonRpc.Data -{ - using Nethermind.JsonRpc.Modules.Trace; - - [JsonConverter(typeof(TransactionForRpcWithTraceTypesConverter))] - public class TransactionForRpcWithTraceTypes - { - public TransactionForRpc Transaction { get; set; } - public string[] TraceTypes { get; set; } - } -} +namespace Nethermind.JsonRpc.Data; -namespace Nethermind.JsonRpc.Modules.Trace +[JsonConverter(typeof(TransactionForRpcWithTraceTypesConverter))] +public class TransactionForRpcWithTraceTypes { - using Nethermind.JsonRpc.Data; - public class TransactionForRpcWithTraceTypesConverter : JsonConverter + public TransactionForRpc Transaction { get; set; } + public string[] TraceTypes { get; set; } + + private class TransactionForRpcWithTraceTypesConverter : JsonConverter { public override TransactionForRpcWithTraceTypes? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { @@ -31,6 +24,7 @@ public class TransactionForRpcWithTraceTypesConverter : JsonConverter(ref reader, options); @@ -43,8 +37,6 @@ public class TransactionForRpcWithTraceTypesConverter : JsonConverter throw new NotSupportedException(); } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs index 9c0b2919d32..aa0318197c6 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs @@ -18,7 +18,7 @@ using System.Collections.Generic; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.Core.Specs; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; namespace Nethermind.JsonRpc.Modules.DebugModule; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs index dc0a8bd9014..f8c1129cddc 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs @@ -6,7 +6,7 @@ using Nethermind.Blockchain.Find; using Nethermind.Core.Crypto; using Nethermind.Evm.Tracing.GethStyle; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.Synchronization.Reporting; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs index 6da17831925..146df3f42a2 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs @@ -1,17 +1,13 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using Nethermind.Blockchain.Find; using Nethermind.Core; -using Nethermind.Core.Eip2930; using Nethermind.Core.Extensions; using Nethermind.Evm; using Nethermind.Facade; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; using Nethermind.JsonRpc.Data; using Nethermind.Specs.Forks; @@ -27,7 +23,12 @@ private abstract class TxExecutor(IBlockchainBridge blockchainBridge, I { private bool NoBaseFee { get; set; } - protected override Transaction Prepare(TransactionForRpc call) => call.ToTransaction(_blockchainBridge.GetChainId()); + protected override Transaction Prepare(TransactionForRpc call) + { + var tx = call.ToTransaction(); + tx.ChainId = _blockchainBridge.GetChainId(); + return tx; + } protected override ResultWrapper Execute(BlockHeader header, Transaction tx, CancellationToken token) { @@ -43,15 +44,11 @@ protected override ResultWrapper Execute(BlockHeader header, Transactio return ExecuteTx(clonedHeader, tx, token); } - private static bool ShouldSetBaseFee(TransactionForRpc t) => - // x?.IsZero == false <=> x > 0 - t.GasPrice?.IsZero == false || t.MaxFeePerGas?.IsZero == false || t.MaxPriorityFeePerGas?.IsZero == false; - public override ResultWrapper Execute( TransactionForRpc transactionCall, BlockParameter? blockParameter) { - NoBaseFee = !ShouldSetBaseFee(transactionCall); + NoBaseFee = !transactionCall.ShouldSetBaseFee(); transactionCall.EnsureDefaults(_rpcConfig.GasCap); return base.Execute(transactionCall, blockParameter); } @@ -59,9 +56,6 @@ public override ResultWrapper Execute( public ResultWrapper ExecuteTx(TransactionForRpc transactionCall, BlockParameter? blockParameter) => Execute(transactionCall, blockParameter); protected abstract ResultWrapper ExecuteTx(BlockHeader header, Transaction tx, CancellationToken token); - - protected ResultWrapper GetInputError(CallOutput result) => - ResultWrapper.Fail(result.Error, ErrorCodes.InvalidInput); } private class CallTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig) @@ -87,38 +81,32 @@ private class EstimateGasTxExecutor(IBlockchainBridge blockchainBridge, IBlockFi { CallOutput result = _blockchainBridge.EstimateGas(header, tx, _errorMargin, token); - if (result.Error is null) + return result switch { - return ResultWrapper.Success((UInt256)result.GasSpent); - } - - return result.InputError - ? GetInputError(result) - : ResultWrapper.Fail(result.Error, ErrorCodes.ExecutionError); + { Error: null } => ResultWrapper.Success((UInt256)result.GasSpent), + { InputError: true } => ResultWrapper.Fail(result.Error, ErrorCodes.InvalidInput), + _ => ResultWrapper.Fail(result.Error, ErrorCodes.ExecutionError) + }; } } private class CreateAccessListTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig, bool optimize) - : TxExecutor(blockchainBridge, blockFinder, rpcConfig) + : TxExecutor(blockchainBridge, blockFinder, rpcConfig) { - protected override ResultWrapper ExecuteTx(BlockHeader header, Transaction tx, CancellationToken token) + protected override ResultWrapper ExecuteTx(BlockHeader header, Transaction tx, CancellationToken token) { CallOutput result = _blockchainBridge.CreateAccessList(header, tx, token, optimize); - if (result.Error is null) - { - return ResultWrapper.Success(new(GetResultAccessList(tx, result), GetResultGas(tx, result))); - } - - return result.InputError - ? GetInputError(result) - : ResultWrapper.Fail(result.Error, ErrorCodes.ExecutionError, new AccessListForRpc(GetResultAccessList(tx, result), GetResultGas(tx, result))); - } + var rpcAccessListResult = new AccessListResultForRpc( + accessList: AccessListForRpc.FromAccessList(result.AccessList ?? tx.AccessList), + gasUsed: GetResultGas(tx, result)); - private static IEnumerable GetResultAccessList(Transaction tx, CallOutput result) - { - AccessList? accessList = result.AccessList ?? tx.AccessList; - return accessList is null ? Enumerable.Empty() : AccessListItemForRpc.FromAccessList(accessList); + return result switch + { + { Error: null } => ResultWrapper.Success(rpcAccessListResult), + { InputError: true } => ResultWrapper.Fail(result.Error, ErrorCodes.InvalidInput), + _ => ResultWrapper.Fail(result.Error, ErrorCodes.ExecutionError), + }; } private static UInt256 GetResultGas(Transaction transaction, CallOutput result) diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs index 2a56b3ce014..97246ca48de 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs @@ -20,6 +20,7 @@ using Nethermind.Evm; using Nethermind.Facade; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Filters; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Int256; @@ -280,8 +281,12 @@ public ResultWrapper eth_sign(Address addressData, byte[] message) public virtual Task> eth_sendTransaction(TransactionForRpc rpcTx) { - Transaction tx = rpcTx.ToTransactionWithDefaults(_blockchainBridge.GetChainId()); - TxHandlingOptions options = rpcTx.Nonce is null ? TxHandlingOptions.ManagedNonce : TxHandlingOptions.None; + Transaction tx = rpcTx.ToTransaction(); + tx.ChainId = _blockchainBridge.GetChainId(); + + UInt256? nonce = rpcTx is LegacyTransactionForRpc legacy ? legacy.Nonce : null; + + TxHandlingOptions options = nonce is null ? TxHandlingOptions.ManagedNonce : TxHandlingOptions.None; return SendTx(tx, options); } @@ -334,7 +339,7 @@ public ResultWrapper> eth_simulateV1(Simulate new EstimateGasTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig) .ExecuteTx(transactionCall, blockParameter); - public ResultWrapper eth_createAccessList(TransactionForRpc transactionCall, BlockParameter? blockParameter = null, bool optimize = true) => + public ResultWrapper eth_createAccessList(TransactionForRpc transactionCall, BlockParameter? blockParameter = null, bool optimize = true) => new CreateAccessListTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig, optimize) .ExecuteTx(transactionCall, blockParameter); @@ -377,7 +382,7 @@ public ResultWrapper eth_getBlockByNumber(BlockParameter blockParam } RecoverTxSenderIfNeeded(transaction); - TransactionForRpc transactionModel = new(receipt?.BlockHash, receipt?.BlockNumber, receipt?.Index, transaction, baseFee); + TransactionForRpc transactionModel = TransactionForRpc.FromTransaction(transaction, receipt?.BlockHash, receipt?.BlockNumber, receipt?.Index, baseFee, specProvider.ChainId); if (_logger.IsTrace) _logger.Trace($"eth_getTransactionByHash request {transactionHash}, result: {transactionModel.Hash}"); return ResultWrapper.Success(transactionModel); } @@ -404,7 +409,7 @@ public ResultWrapper eth_pendingTransactions() { Transaction transaction = transactions[i]; RecoverTxSenderIfNeeded(transaction); - transactionsModels[i] = new TransactionForRpc(transaction); + transactionsModels[i] = TransactionForRpc.FromTransaction(transaction, chainId: specProvider.ChainId); transactionsModels[i].BlockHash = Keccak.Zero; } @@ -430,7 +435,7 @@ public ResultWrapper eth_getTransactionByBlockHashAndIndex(Ha Transaction transaction = block.Transactions[(int)positionIndex]; RecoverTxSenderIfNeeded(transaction); - TransactionForRpc transactionModel = new(block.Hash, block.Number, (int)positionIndex, transaction, block.BaseFeePerGas); + TransactionForRpc transactionModel = TransactionForRpc.FromTransaction(transaction, block.Hash, block.Number, (int)positionIndex, block.BaseFeePerGas, specProvider.ChainId); return ResultWrapper.Success(transactionModel); } @@ -453,7 +458,7 @@ public ResultWrapper eth_getTransactionByBlockNumberAndIndex( Transaction transaction = block.Transactions[(int)positionIndex]; RecoverTxSenderIfNeeded(transaction); - TransactionForRpc transactionModel = new(block.Hash, block.Number, (int)positionIndex, transaction, block.BaseFeePerGas); + TransactionForRpc transactionModel = TransactionForRpc.FromTransaction(transaction, block.Hash, block.Number, (int)positionIndex, block.BaseFeePerGas, specProvider.ChainId); if (_logger.IsDebug) _logger.Debug( diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs index 2915d9abcf6..3ded7a3a957 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs @@ -7,10 +7,9 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Filters; using Nethermind.Facade.Proxy.Models.Simulate; -using Nethermind.Facade.Proxy.Models; -using Nethermind.Facade.Proxy; using Nethermind.Int256; using Nethermind.JsonRpc.Data; using Nethermind.State.Proofs; @@ -171,7 +170,7 @@ ResultWrapper> eth_simulateV1([JsonRpcParamet 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)")] diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs index c0bf2db8e26..d87ae39bd1f 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs @@ -3,18 +3,15 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using Nethermind.Blockchain.Find; using Nethermind.Config; using Nethermind.Core; -using Nethermind.Core.Collections; using Nethermind.Facade; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Facade.Proxy.Models.Simulate; using Nethermind.Facade.Simulate; -using Nethermind.JsonRpc.Data; namespace Nethermind.JsonRpc.Modules.Eth; @@ -44,14 +41,15 @@ protected override SimulatePayload Prepare(Simulat StateOverrides = blockStateCall.StateOverrides, Calls = blockStateCall.Calls?.Select(callTransactionModel => { - UpdateTxType(callTransactionModel); + callTransactionModel = UpdateTxType(callTransactionModel); + LegacyTransactionForRpc asLegacy = callTransactionModel as LegacyTransactionForRpc; + bool hadGasLimitInRequest = asLegacy?.Gas is not null; + bool hadNonceInRequest = asLegacy?.Nonce is not null; + asLegacy!.EnsureDefaults(_gasCapBudget); + _gasCapBudget -= asLegacy.Gas!.Value; - bool hadGasLimitInRequest = callTransactionModel.Gas.HasValue; - bool hadNonceInRequest = callTransactionModel.Nonce.HasValue; - callTransactionModel.EnsureDefaults(_gasCapBudget); - _gasCapBudget -= callTransactionModel.Gas!.Value; - - Transaction tx = callTransactionModel.ToTransaction(_blockchainBridge.GetChainId()); + Transaction tx = callTransactionModel.ToTransaction(); + tx.ChainId = _blockchainBridge.GetChainId(); TransactionWithSourceDetails? result = new() { @@ -69,17 +67,28 @@ protected override SimulatePayload Prepare(Simulat return result; } - private static void UpdateTxType(TransactionForRpc callTransactionModel) + private static TransactionForRpc UpdateTxType(TransactionForRpc rpcTransaction) { - if (callTransactionModel.Type == TxType.Legacy) + // TODO: This is a bit messy since we're changing the transaction type + if (rpcTransaction is LegacyTransactionForRpc legacy) { - callTransactionModel.Type = TxType.EIP1559; + rpcTransaction = new EIP1559TransactionForRpc + { + Nonce = legacy.Nonce, + To = legacy.To, + From = legacy.From, + Gas = legacy.Gas, + Value = legacy.Value, + Input = legacy.Input, + GasPrice = legacy.GasPrice, + ChainId = legacy.ChainId, + V = legacy.V, + R = legacy.R, + S = legacy.S, + }; } - if (callTransactionModel.BlobVersionedHashes is not null) - { - callTransactionModel.Type = TxType.Blob; - } + return rpcTransaction; } public override ResultWrapper> Execute( @@ -188,7 +197,7 @@ .. call.BlockStateCalls.Select(b => (long)(b.BlockOverrides?.Number ?? ulong.Min { BlockOverrides = new BlockOverride { Number = (ulong)blockNumber }, StateOverrides = null, - Calls = Array.Empty() + Calls = [] }); } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/IPersonalRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/IPersonalRpcModule.cs index 3ca1db260a6..81805f149bd 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/IPersonalRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/IPersonalRpcModule.cs @@ -3,8 +3,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; namespace Nethermind.JsonRpc.Modules.Personal { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/PersonalRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/PersonalRpcModule.cs index 1116810731d..db941f73ed6 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/PersonalRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Personal/PersonalRpcModule.cs @@ -7,8 +7,7 @@ using Nethermind.Core.Attributes; using Nethermind.Core.Crypto; using Nethermind.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.KeyStore; using Nethermind.Wallet; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/IProofRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/IProofRpcModule.cs index 8e2123e5533..353102d9037 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/IProofRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/IProofRpcModule.cs @@ -3,8 +3,7 @@ using Nethermind.Blockchain.Find; using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; namespace Nethermind.JsonRpc.Modules.Proof { @@ -21,7 +20,7 @@ public interface IProofRpcModule : IRpcModule Description = "This function returns the same result as `eth_getTransactionReceipt` and also a tx proof, receipt proof and serialized block headers.", IsSharable = false, ExampleResponse = "\"transaction\":{\"hash\":\"0xb62594c08de66c683fbffe44792a1ccc0f9b80e43071048ed03c18a71fd3c19a\",\"nonce\":\"0x630\",\"blockHash\":\"0x42d72739c2b2659916d7b42a49661fdec317e780af1395c2c15aa89b4c42e220\",\"blockNumber\":\"0x88f194\",\"transactionIndex\":\"0x24\",\"from\":\"0x78ca86e8133ef9368b4537879cf2f38fddbb636b\",\"to\":\"0x1dfd95eb75a7486945d366a0bc0b937f0aaa526f\",\"value\":\"0x0\",\"gasPrice\":\"0x3b9aca00\",\"gas\":\"0xc9e2\",\"data\":\"0xa9059cbb000000000000000000000000e3ac1cc1453e70f80ff58f3bb56b0532238ae24a00000000000000000000000000000000000000000000003635c9adc5dea00000\",\"input\":\"0xa9059cbb000000000000000000000000e3ac1cc1453e70f80ff58f3bb56b0532238ae24a00000000000000000000000000000000000000000000003635c9adc5dea00000\",\"type\":\"0x0\",\"v\":\"0x2b\",\"s\":\"0x33a9425e84bf310d372a9f531b237baebccfdd2b426e817cc9553355a9165342\",\"r\":\"0xe14a066de4787a4c0192f5a2285fd835a85baa3a4f63b1e8a2d8d7f6e04425ca\"},\"txProof\":[\"0xf891a0311d3b27b7612bf40c2c5d623c62c2afe30a47f486700074e4c4d7cf603c90c8a0cd64d350a95e9286a580a75ae11fe58801992f9ac65ace8a0b853d16f87b09b0a0ae9d609ff06d19bb911d7ad05cfdd6c80a9f1fddccbdb76a78594536122345ce8080808080a09773b23452983c0ed65aebb64522af322967c62be34414e16b32b7e4bdaecdb68080808080808080\",\"0xf8b1a0715f91aae7675a1c8469685d18bc94241d275c82a3b52df6c4fab064fcba3017a0e77ac7615c08eaafccc876956f3dad1892f08c1f1128e2cdf9064664381a540fa06f2d934e5f7995657144ad66b8b5cdce6b6c141422f95d44eb91ca6765d4f819a0b265c005bad056db029945b3d68a631b624a77703733fa9b2042c0f211f8ef4ea0bb97f719cc5f6082fe5bab8588dc564a843a6b40c5494982ded868f19eef07b6808080808080808080808080\",\"0xf8af20b8acf8aa820630843b9aca0082c9e2941dfd95eb75a7486945d366a0bc0b937f0aaa526f80b844a9059cbb000000000000000000000000e3ac1cc1453e70f80ff58f3bb56b0532238ae24a00000000000000000000000000000000000000000000003635c9adc5dea000002ba0e14a066de4787a4c0192f5a2285fd835a85baa3a4f63b1e8a2d8d7f6e04425caa033a9425e84bf310d372a9f531b237baebccfdd2b426e817cc9553355a9165342\"]")] - ResultWrapper proof_getTransactionByHash([JsonRpcParameter(ExampleValue = "\"[\"0xb62594c08de66c683fbffe44792a1ccc0f9b80e43071048ed03c18a71fd3c19a\", \"false\"]")] Hash256 txHash, bool includeHeader); + ResultWrapper proof_getTransactionByHash([JsonRpcParameter(ExampleValue = "\"[\"0xb62594c08de66c683fbffe44792a1ccc0f9b80e43071048ed03c18a71fd3c19a\", \"false\"]")] Hash256 txHash, bool includeHeader); [JsonRpcMethod(IsImplemented = true, Description = "This function should return the same result as `eth_call` and also proofs of all used accounts and their storages and serialized block headers.", diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofRpcModule.cs index e79f6fdd106..5818849ea3a 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofRpcModule.cs @@ -14,7 +14,7 @@ using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.Evm.Tracing.Proofs; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; using Nethermind.Logging; using Nethermind.Serialization.Rlp; @@ -102,18 +102,18 @@ public ResultWrapper proof_call(TransactionForRpc tx, Block return ResultWrapper.Success(callResultWithProof); } - public ResultWrapper proof_getTransactionByHash(Hash256 txHash, bool includeHeader) + public ResultWrapper proof_getTransactionByHash(Hash256 txHash, bool includeHeader) { Hash256 blockHash = _receiptFinder.FindBlockHash(txHash); if (blockHash is null) { - return ResultWrapper.Fail($"{txHash} receipt (transaction) could not be found", ErrorCodes.ResourceNotFound); + return ResultWrapper.Fail($"{txHash} receipt (transaction) could not be found", ErrorCodes.ResourceNotFound); } SearchResult searchResult = _blockFinder.SearchForBlock(new BlockParameter(blockHash)); if (searchResult.IsError) { - return ResultWrapper.Fail(searchResult); + return ResultWrapper.Fail(searchResult); } Block block = searchResult.Object; @@ -121,15 +121,15 @@ public ResultWrapper proof_getTransactionByHash(Hash256 tx Transaction[] txs = block.Transactions; Transaction transaction = txs[receipt.Index]; - TransactionWithProof txWithProof = new(); - txWithProof.Transaction = new TransactionForRpc(block.Hash, block.Number, receipt.Index, transaction, block.BaseFeePerGas); + TransactionForRpcWithProof txWithProof = new(); + txWithProof.Transaction = TransactionForRpc.FromTransaction(transaction, block.Hash, block.Number, receipt.Index, block.BaseFeePerGas, _specProvider.ChainId); txWithProof.TxProof = BuildTxProofs(txs, _specProvider.GetSpec(block.Header), receipt.Index); if (includeHeader) { txWithProof.BlockHeader = _headerDecoder.Encode(block.Header).Bytes; } - return ResultWrapper.Success(txWithProof); + return ResultWrapper.Success(txWithProof); } public ResultWrapper proof_getTransactionReceipt(Hash256 txHash, bool includeHeader) diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionForRpcWithProof.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionForRpcWithProof.cs new file mode 100644 index 00000000000..3d3745fafd7 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionForRpcWithProof.cs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Facade.Eth.RpcTransaction; + +namespace Nethermind.JsonRpc.Modules.Proof; + +public class TransactionForRpcWithProof +{ + public TransactionForRpc Transaction { get; set; } + + public byte[][] TxProof { get; set; } + + public byte[] BlockHeader { get; set; } +} diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionWithProof.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionWithProof.cs deleted file mode 100644 index 3069c957849..00000000000 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/TransactionWithProof.cs +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; - -namespace Nethermind.JsonRpc.Modules.Proof -{ - public class TransactionWithProof - { - public TransactionForRpc Transaction { get; set; } - - public byte[][] TxProof { get; set; } - - public byte[] BlockHeader { get; set; } - } -} diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/NewPendingTransactionsSubscription.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/NewPendingTransactionsSubscription.cs index 7ec4b75d77d..ebf4ec7267a 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/NewPendingTransactionsSubscription.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/NewPendingTransactionsSubscription.cs @@ -2,9 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Threading.Tasks; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Core.Specs; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Modules.Eth; using Nethermind.Logging; using Nethermind.TxPool; @@ -14,16 +13,19 @@ namespace Nethermind.JsonRpc.Modules.Subscribe public class NewPendingTransactionsSubscription : Subscription { private readonly ITxPool _txPool; + private readonly ISpecProvider _specProvider; private readonly bool _includeTransactions; public NewPendingTransactionsSubscription( IJsonRpcDuplexClient jsonRpcDuplexClient, ITxPool? txPool, + ISpecProvider? specProvider, ILogManager? logManager, TransactionsOption? options = null) : base(jsonRpcDuplexClient) { _txPool = txPool ?? throw new ArgumentNullException(nameof(txPool)); + _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _includeTransactions = options?.IncludeTransactions ?? false; @@ -35,7 +37,9 @@ private void OnNewPending(object? sender, TxEventArgs e) { ScheduleAction(async () => { - using JsonRpcResult result = CreateSubscriptionMessage(_includeTransactions ? new TransactionForRpc(e.Transaction) : e.Transaction.Hash); + using JsonRpcResult result = CreateSubscriptionMessage(_includeTransactions + ? TransactionForRpc.FromTransaction(e.Transaction, chainId: _specProvider.ChainId) + : e.Transaction.Hash!); await JsonRpcDuplexClient.SendJsonRpcResult(result); if (_logger.IsTrace) _logger.Trace($"NewPendingTransactions subscription {Id} printed hash of NewPendingTransaction."); }); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactory.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactory.cs index 29779ac837e..00abc7a7e00 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactory.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactory.cs @@ -61,7 +61,7 @@ public SubscriptionFactory(ILogManager? logManager, new LogsSubscription(jsonRpcDuplexClient, receiptCanonicalityMonitor, filterStore, blockTree, logManager, filter)), [SubscriptionType.NewPendingTransactions] = CreateSubscriptionType((jsonRpcDuplexClient, args) => - new NewPendingTransactionsSubscription(jsonRpcDuplexClient, txPool, logManager, args)), + new NewPendingTransactionsSubscription(jsonRpcDuplexClient, txPool, specProvider, logManager, args)), [SubscriptionType.DroppedPendingTransactions] = CreateSubscriptionType(jsonRpcDuplexClient => new DroppedPendingTransactionsSubscription(jsonRpcDuplexClient, txPool, logManager)), diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs index dae0f773487..80318f35c2b 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Nethermind.Blockchain.Find; using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.JsonRpc.Data; namespace Nethermind.JsonRpc.Modules.Trace diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs index f7747d6436f..622ba612700 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs @@ -11,17 +11,14 @@ using Nethermind.Consensus.Tracing; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Specs; using Nethermind.Evm.Tracing; using Nethermind.Evm.Tracing.ParityStyle; using Nethermind.Facade; -using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; using Nethermind.JsonRpc.Data; -using Nethermind.Logging; using Nethermind.Serialization.Rlp; using Nethermind.State; -using Nethermind.Trie; namespace Nethermind.JsonRpc.Modules.Trace { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs index 2040cc403e8..a4726b89477 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TransactionPoolContent.cs @@ -4,18 +4,17 @@ using System.Collections.Generic; using System.Linq; using Nethermind.Core; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.TxPool; namespace Nethermind.JsonRpc.Modules.TxPool { public class TxPoolContent { - public TxPoolContent(TxPoolInfo info) + public TxPoolContent(TxPoolInfo info, ulong chainId) { - Pending = info.Pending.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => new TransactionForRpc(null, null, null, v.Value))); - Queued = info.Queued.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => new TransactionForRpc(null, null, null, v.Value))); + Pending = info.Pending.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => TransactionForRpc.FromTransaction(v.Value, chainId: chainId))); + Queued = info.Queued.ToDictionary(k => k.Key, k => k.Value.ToDictionary(v => v.Key, v => TransactionForRpc.FromTransaction(v.Value, chainId: chainId))); } public Dictionary> Pending { get; set; } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs index da0a5893561..02c90681997 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/TxPool/TxPoolRpcModule.cs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using Nethermind.Logging; +using Nethermind.Core.Specs; using Nethermind.TxPool; namespace Nethermind.JsonRpc.Modules.TxPool @@ -10,10 +10,12 @@ namespace Nethermind.JsonRpc.Modules.TxPool public class TxPoolRpcModule : ITxPoolRpcModule { private readonly ITxPoolInfoProvider _txPoolInfoProvider; + private readonly ISpecProvider _specProvider; - public TxPoolRpcModule(ITxPoolInfoProvider txPoolInfoProvider, ILogManager logManager) + public TxPoolRpcModule(ITxPoolInfoProvider txPoolInfoProvider, ISpecProvider specProvider) { _txPoolInfoProvider = txPoolInfoProvider ?? throw new ArgumentNullException(nameof(txPoolInfoProvider)); + _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); } public ResultWrapper txpool_status() @@ -27,7 +29,8 @@ public ResultWrapper txpool_status() public ResultWrapper txpool_content() { var poolInfo = _txPoolInfoProvider.GetInfo(); - return ResultWrapper.Success(new TxPoolContent(poolInfo)); + var chainId = _specProvider.ChainId; + return ResultWrapper.Success(new TxPoolContent(poolInfo, chainId)); } public ResultWrapper txpool_inspect() diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs index 6f3bf5874ef..fc4e6d663ca 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs @@ -244,8 +244,7 @@ public async Task NewPayloadV3_should_decline_null_blobversionedhashes() string executionPayloadString = serializer.Serialize(executionPayload); - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - executionPayloadString, null!); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), executionPayloadString, null!); JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse; Assert.That(response?.Error, Is.Not.Null); Assert.That(response!.Error!.Code, Is.EqualTo(ErrorCodes.InvalidParams)); @@ -260,8 +259,7 @@ public async Task NewPayloadV3_invalidblockhash() string requestStr = """ {"parentHash":"0xd6194b42ad579c195e9aaaf04692619f4de9c5fbdd6b58baaabe93384e834d25","feeRecipient":"0x0000000000000000000000000000000000000000","stateRoot":"0xfe1fa6bb862e4a5efd9ee8967b356d4f7b6205a437eeac8b0e625db3cb662018","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prevRandao":"0xfaebfae9aef88ac8eba03decf329c8155991e07cb1a9dc6ac69e420550a45037","blockNumber":"0x1","gasLimit":"0x2fefd8","gasUsed":"0x0","timestamp":"0x1235","extraData":"0x4e65746865726d696e64","baseFeePerGas":"0x342770c0","blockHash":"0x0718890af079939b4aae6ac0ecaa94c633ad73e69e787f526f0d558043e8e2f1","transactions":[],"withdrawals":[{"index":"0x1","validatorIndex":"0x0","address":"0x0000000000000000000000000000000000000000","amount":"0x64"},{"index":"0x2","validatorIndex":"0x1","address":"0x0100000000000000000000000000000000000000","amount":"0x64"},{"index":"0x3","validatorIndex":"0x2","address":"0x0200000000000000000000000000000000000000","amount":"0x64"},{"index":"0x4","validatorIndex":"0x3","address":"0x0300000000000000000000000000000000000000","amount":"0x64"},{"index":"0x5","validatorIndex":"0x4","address":"0x0400000000000000000000000000000000000000","amount":"0x64"},{"index":"0x6","validatorIndex":"0x5","address":"0x0500000000000000000000000000000000000000","amount":"0x64"},{"index":"0x7","validatorIndex":"0x6","address":"0x0600000000000000000000000000000000000000","amount":"0x64"},{"index":"0x8","validatorIndex":"0x7","address":"0x0700000000000000000000000000000000000000","amount":"0x64"},{"index":"0x9","validatorIndex":"0x8","address":"0x0800000000000000000000000000000000000000","amount":"0x64"},{"index":"0xa","validatorIndex":"0x9","address":"0x0900000000000000000000000000000000000000","amount":"0x64"}],"excessBlobGas":"0x0"} """; - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - requestStr, "[]", "0x169630f535b4a41330164c6e5c92b1224c0c407f582d407d0ac3d206cd32fd52"); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), requestStr, "[]", "0x169630f535b4a41330164c6e5c92b1224c0c407f582d407d0ac3d206cd32fd52"); var rpcResponse = await jsonRpcService.SendRequestAsync(request, context); JsonRpcErrorResponse? response = (rpcResponse) as JsonRpcErrorResponse; @@ -296,8 +294,7 @@ public async Task NewPayloadV3_should_decline_empty_fields() { JsonObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString); - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - serializer.Serialize(executionPayloadAsJObject), blobsString, parentBeaconBlockRootString); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), serializer.Serialize(executionPayloadAsJObject), blobsString, parentBeaconBlockRootString); JsonRpcResponse response = await jsonRpcService.SendRequestAsync(request, context); Assert.That(response is JsonRpcSuccessResponse); } @@ -310,8 +307,7 @@ public async Task NewPayloadV3_should_decline_empty_fields() JsonObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString); executionPayloadAsJObject[prop] = null; - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - serializer.Serialize(executionPayloadAsJObject), blobsString); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), serializer.Serialize(executionPayloadAsJObject), blobsString); JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse; Assert.That(response?.Error, Is.Not.Null); Assert.That(response!.Error!.Code, Is.EqualTo(ErrorCodes.InvalidParams)); @@ -322,8 +318,7 @@ public async Task NewPayloadV3_should_decline_empty_fields() JsonObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString); executionPayloadAsJObject.Remove(prop); - JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), - serializer.Serialize(executionPayloadAsJObject), blobsString); + JsonRpcRequest request = RpcTest.BuildJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3), serializer.Serialize(executionPayloadAsJObject), blobsString); JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse; Assert.That(response?.Error, Is.Not.Null); Assert.That(response!.Error!.Code, Is.EqualTo(ErrorCodes.InvalidParams)); diff --git a/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismTransactionForRpcTests.cs b/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismTransactionForRpcTests.cs new file mode 100644 index 00000000000..464d0451c5c --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismTransactionForRpcTests.cs @@ -0,0 +1,109 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json; +using NUnit.Framework; +using Nethermind.Serialization.Json; +using Nethermind.Optimism.Rpc; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Core; +using FluentAssertions; +using Nethermind.Core.Test.Builders; +using Nethermind.Core.Crypto; +using System; +using System.Collections.Generic; + +namespace Nethermind.Optimism.Test.Rpc; + +public class OptimismTransactionForRpcTests +{ + private readonly IJsonSerializer serializer = new EthereumJsonSerializer(); + + private static TransactionBuilder Build => Core.Test.Builders.Build.A.Transaction.WithType(TxType.DepositTx); + public static readonly Transaction[] Transactions = [ + Build.TestObject, + Build + .With(s => s.IsOPSystemTransaction = true) + .With(s => s.Mint = 1234) + .TestObject, + Build + .WithGasLimit(0x1234) + .WithValue(0x1) + .WithData([0x61, 0x62, 0x63, 0x64, 0x65, 0x66]) + .WithSourceHash(Hash256.Zero) + .WithSenderAddress(Address.FromNumber(1)) + .WithHash(new Hash256("0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9")) + .TestObject + ]; + + [SetUp] + public void SetUp() + { + TransactionForRpc.RegisterTransactionType(); + } + + [TestCaseSource(nameof(Transactions))] + public void Always_satisfies_schema(Transaction transaction) + { + TransactionForRpc rpcTransaction = TransactionForRpc.FromTransaction(transaction); + string serialized = serializer.Serialize(rpcTransaction); + using var jsonDocument = JsonDocument.Parse(serialized); + JsonElement json = jsonDocument.RootElement; + ValidateSchema(json); + } + + private static void ValidateSchema(JsonElement json) + { + json.GetProperty("type").GetString().Should().MatchRegex("^0x7[eE]$"); + json.GetProperty("sourceHash").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{64}$"); + json.GetProperty("from").GetString().Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + json.GetProperty("to").GetString()?.Should().MatchRegex("^0x[0-9a-fA-F]{40}$"); + var hasMint = json.TryGetProperty("mint", out var mint); + if (hasMint) + { + mint.GetString()?.Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } + json.GetProperty("value").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + json.GetProperty("gas").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + var hasIsSystemTx = json.TryGetProperty("isSystemTx", out var isSystemTx); + if (hasIsSystemTx) + { + isSystemTx.GetBoolean(); + } + json.GetProperty("input").GetString().Should().MatchRegex("^0x[0-9a-f]*$"); + json.GetProperty("nonce").GetString().Should().MatchRegex("^0x([1-9a-f]+[0-9a-f]*|0)$"); + } + + private static readonly IEnumerable<(string, string)> MalformedJsonTransactions = [ + (nameof(OptimismTransactionForRpc.Gas), """{"type":"0x7e","nonce":null,"gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.Value), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.Input), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.From), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.SourceHash), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + ]; + + [TestCaseSource(nameof(MalformedJsonTransactions))] + public void Rejects_malformed_transaction_missing_field((string missingField, string json) testCase) + { + var rpcTx = serializer.Deserialize(testCase.json); + rpcTx.Should().NotBeNull(); + + var toTransaction = rpcTx.ToTransaction; + toTransaction.Should().Throw().WithParameterName(testCase.missingField); + } + + private static readonly IEnumerable<(string, string)> ValidJsonTransactions = [ + (nameof(OptimismTransactionForRpc.Mint), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","isSystemTx":false,"hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + (nameof(OptimismTransactionForRpc.IsSystemTx), """{"type":"0x7e","nonce":null,"gas": "0x1234", "gasPrice":null,"maxPriorityFeePerGas":null,"maxFeePerGas":null,"value":"0x1","input":"0x616263646566","v":null,"r":null,"s":null,"to":null,"sourceHash":"0x0000000000000000000000000000000000000000000000000000000000000000","from":"0x0000000000000000000000000000000000000001","hash":"0xa4341f3db4363b7ca269a8538bd027b2f8784f84454ca917668642d5f6dffdf9"}"""), + ]; + + [TestCaseSource(nameof(ValidJsonTransactions))] + public void Accepts_valid_transaction_missing_field((string missingField, string json) testCase) + { + var rpcTx = serializer.Deserialize(testCase.json); + rpcTx.Should().NotBeNull(); + + var toTransaction = rpcTx.ToTransaction; + toTransaction.Should().NotThrow(); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs b/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs index 32eaa382de9..8410ba003b5 100644 --- a/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs +++ b/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs @@ -11,10 +11,12 @@ using Nethermind.Consensus.Producers; using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; +using Nethermind.Core; using Nethermind.Evm; using Nethermind.Evm.TransactionProcessing; using Nethermind.Init.Steps; using Nethermind.Merge.Plugin.InvalidChainTracker; +using Nethermind.Optimism.Rpc; namespace Nethermind.Optimism; @@ -24,6 +26,8 @@ public class InitializeBlockchainOptimism(OptimismNethermindApi api) : Initializ protected override async Task InitBlockchain() { + api.RegisterTxType(new OptimismTxDecoder(), Always.Valid); + api.SpecHelper = new(api.ChainSpec.Optimism); api.L1CostHelper = new(api.SpecHelper, api.ChainSpec.Optimism.L1BlockAddress); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index 82d7d0665c4..0517d73f040 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -80,7 +80,7 @@ public void InitTxTypesAndRlpDecoders(INethermindApi api) { if (ShouldRunSteps(api)) { - api.RegisterTxType(TxType.DepositTx, new OptimismTxDecoder(), Always.Valid); + api.RegisterTxType(new OptimismTxDecoder(), Always.Valid); Rlp.RegisterDecoders(typeof(OptimismReceiptMessageDecoder).Assembly, true); } } diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs index 8a3d5586391..e5933bb58f6 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs @@ -12,6 +12,7 @@ using Nethermind.Evm; using Nethermind.Facade; using Nethermind.Facade.Eth; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Int256; using Nethermind.JsonRpc; using Nethermind.JsonRpc.Client; @@ -108,7 +109,8 @@ public OptimismEthRpcModule( public override async Task> eth_sendTransaction(TransactionForRpc rpcTx) { - Transaction tx = rpcTx.ToTransactionWithDefaults(_blockchainBridge.GetChainId()); + Transaction tx = rpcTx.ToTransaction(); + tx.ChainId = _blockchainBridge.GetChainId(); tx.SenderAddress ??= _ecdsa.RecoverAddress(tx); if (tx.SenderAddress is null) @@ -116,11 +118,6 @@ public override async Task> eth_sendTransaction(Transacti return ResultWrapper.Fail("Failed to recover sender"); } - if (rpcTx.Nonce is null) - { - tx.Nonce = _accountStateProvider.GetNonce(tx.SenderAddress); - } - await _sealer.Seal(tx, TxHandlingOptions.None); return await eth_sendRawTransaction(Rlp.Encode(tx, RlpBehaviors.SkipTypedWrapping).Bytes); @@ -165,20 +162,19 @@ public override async Task> eth_sendRawTransaction(byte[] public new ResultWrapper eth_getTransactionByHash(Hash256 transactionHash) { - (TxReceipt? receipt, Transaction? transaction, UInt256? baseFee) = _blockchainBridge.GetTransaction(transactionHash, checkTxnPool: true); + (TxReceipt? receipt, Transaction? transaction, _) = _blockchainBridge.GetTransaction(transactionHash, checkTxnPool: true); if (transaction is null) { return ResultWrapper.Success(null); } RecoverTxSenderIfNeeded(transaction); - OptimismTransactionForRpc transactionModel = new(receipt?.BlockHash, receipt as OptimismTxReceipt, transaction, baseFee); + OptimismTransactionForRpc transactionModel = new OptimismTransactionForRpc(transaction, blockHash: receipt?.BlockHash, receipt: receipt as OptimismTxReceipt); if (_logger.IsTrace) _logger.Trace($"eth_getTransactionByHash request {transactionHash}, result: {transactionModel.Hash}"); return ResultWrapper.Success(transactionModel); } - public new ResultWrapper eth_getTransactionByBlockHashAndIndex(Hash256 blockHash, - UInt256 positionIndex) + public new ResultWrapper eth_getTransactionByBlockHashAndIndex(Hash256 blockHash, UInt256 positionIndex) { SearchResult searchResult = _blockFinder.SearchForBlock(new BlockParameter(blockHash)); if (searchResult.IsError || searchResult.Object is null) @@ -196,7 +192,10 @@ public override async Task> eth_sendRawTransaction(byte[] Transaction transaction = block.Transactions[(int)positionIndex]; RecoverTxSenderIfNeeded(transaction); - OptimismTransactionForRpc transactionModel = new(block.Hash, receipts.FirstOrDefault(r => r.TxHash == transaction.Hash), transaction, block.BaseFeePerGas); + OptimismTransactionForRpc transactionModel = new OptimismTransactionForRpc( + transaction, + blockHash: block.Hash, + receipt: receipts.FirstOrDefault(r => r.TxHash == transaction.Hash)); return ResultWrapper.Success(transactionModel); } @@ -222,7 +221,10 @@ public override async Task> eth_sendRawTransaction(byte[] Transaction transaction = block.Transactions[(int)positionIndex]; RecoverTxSenderIfNeeded(transaction); - OptimismTransactionForRpc transactionModel = new(block.Hash, receipts.FirstOrDefault(r => r.TxHash == transaction.Hash), transaction, block.BaseFeePerGas); + OptimismTransactionForRpc transactionModel = new OptimismTransactionForRpc( + transaction, + blockHash: block.Hash, + receipt: receipts.FirstOrDefault(r => r.TxHash == transaction.Hash)); if (_logger.IsDebug) _logger.Debug( @@ -252,7 +254,12 @@ public override async Task> eth_sendRawTransaction(byte[] if (returnFullTransactionObjects) { _blockchainBridge.RecoverTxSenders(block); - result.Transactions = result.Transactions.Select((hash, index) => new OptimismTransactionForRpc(block.Hash, receipts.FirstOrDefault(r => r.TxHash?.Equals(hash) ?? false), block.Transactions[index], block.BaseFeePerGas)); + result.Transactions = result.Transactions.Select((hash, index) => new OptimismTransactionForRpc( + transaction: block.Transactions[index], + blockHash: block.Hash, + blockNumber: block.Number, + txIndex: index, + receipt: receipts.FirstOrDefault(r => r.TxHash?.Equals(hash) ?? false))); } return ResultWrapper.Success(result); diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismTransactionForRpc.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismTransactionForRpc.cs index cb28429f3f6..fba5f35ba18 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismTransactionForRpc.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismTransactionForRpc.cs @@ -3,27 +3,91 @@ using Nethermind.Core.Crypto; using Nethermind.Core; -using Nethermind.Facade.Eth; using Nethermind.Int256; using System.Text.Json.Serialization; +using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Facade.Eth; +using System; namespace Nethermind.Optimism.Rpc; -public class OptimismTransactionForRpc : TransactionForRpc +/// +/// Defined in: +/// - https://github.com/ethereum-optimism/op-geth/blob/8af19cf20261c0b62f98cc27da3a268f542822ee/core/types/deposit_tx.go#L29-L46 +/// - https://specs.optimism.io/protocol/deposits.html#the-deposited-transaction-type +/// +public class OptimismTransactionForRpc : TransactionForRpc, IFromTransaction { - public OptimismTransactionForRpc(Hash256? blockHash, OptimismTxReceipt? receipt, Transaction transaction, UInt256? baseFee = null) - : base(blockHash, receipt?.BlockNumber, receipt?.Index, transaction, baseFee) - { - if (transaction.Type == TxType.DepositTx) - { - SourceHash = transaction.SourceHash; - Mint = transaction.Mint; - IsSystemTx = transaction.IsOPSystemTransaction ? true : null; - Nonce = receipt?.DepositNonce; - DepositReceiptVersion = receipt?.DepositReceiptVersion; - } - } + public static TxType TxType => TxType.DepositTx; + + public override TxType? Type => TxType; + + public Hash256? SourceHash { get; set; } + + public Address? From { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public Address? To { get; set; } + + public UInt256? Mint { get; set; } + + public UInt256? Value { get; set; } + + public ulong? Gas { get; set; } + + public bool? IsSystemTx { get; set; } + + public byte[]? Input { get; set; } + + public UInt256? Nonce { get; set; } + #region Nethermind specific fields [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public UInt256? DepositReceiptVersion { get; set; } + #endregion + + [JsonConstructor] + public OptimismTransactionForRpc() { } + + public OptimismTransactionForRpc(Transaction transaction, int? txIndex = null, Hash256? blockHash = null, long? blockNumber = null, OptimismTxReceipt? receipt = null) + : base(transaction, txIndex, blockHash, blockNumber) + { + SourceHash = transaction.SourceHash ?? Hash256.Zero; + From = transaction.SenderAddress ?? Address.SystemUser; + To = transaction.To; + Mint = transaction.Mint; + Value = transaction.Value; + // TODO: Unsafe cast + Gas = (ulong)transaction.GasLimit; + IsSystemTx = transaction.IsOPSystemTransaction; + Input = transaction.Data?.ToArray() ?? []; + Nonce = receipt?.DepositNonce ?? 0; + + DepositReceiptVersion = receipt?.DepositReceiptVersion; + } + + public override Transaction ToTransaction() + { + var tx = base.ToTransaction(); + + tx.SourceHash = SourceHash ?? throw new ArgumentNullException(nameof(SourceHash)); + tx.SenderAddress = From ?? throw new ArgumentNullException(nameof(From)); + tx.To = To; + tx.Mint = Mint ?? 0; + tx.Value = Value ?? throw new ArgumentNullException(nameof(Value)); + // TODO: Unsafe cast + tx.GasLimit = (long)(Gas ?? throw new ArgumentNullException(nameof(Gas))); + tx.IsOPSystemTransaction = IsSystemTx ?? false; + tx.Data = Input ?? throw new ArgumentNullException(nameof(Input)); + + return tx; + } + + // NOTE: No defaulting mechanism for Optimism transactions + public override void EnsureDefaults(long? gasCap) { } + + public override bool ShouldSetBaseFee() => false; + + public static OptimismTransactionForRpc FromTransaction(Transaction tx, TransactionConverterExtraData extraData) + => new(tx, txIndex: extraData.TxIndex, blockHash: extraData.BlockHash, blockNumber: extraData.BlockNumber, receipt: extraData.Receipt as OptimismTxReceipt); } diff --git a/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs b/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs index bec343e4e35..f1583b6339b 100644 --- a/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs +++ b/src/Nethermind/Nethermind.Overseer.Test/CliqueTests.cs @@ -4,8 +4,7 @@ using System.Threading.Tasks; using Nethermind.Core.Extensions; using Nethermind.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Overseer.Test.Framework; using NUnit.Framework; @@ -72,23 +71,24 @@ public async Task Clique_vote() [Test] public async Task Clique_transaction_broadcast() { - TransactionForRpc tx = new TransactionForRpc(); - tx.Value = 2.Ether(); - tx.GasPrice = 20.GWei(); - tx.Gas = 21000; - tx.From = new PrivateKey(new byte[32] { - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,3 - }).Address; - - tx.To = new PrivateKey(new byte[32] { - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,1 - }).Address; + var tx = new LegacyTransactionForRpc + { + Value = 2.Ether(), + GasPrice = 20.GWei(), + Gas = 21000, + From = new PrivateKey(new byte[32] { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,3 + }).Address, + To = new PrivateKey(new byte[32] { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1 + }).Address + }; StartCliqueMiner("cliqueval1d") .StartCliqueMiner("cliqueval2d") diff --git a/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs b/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs index 9d22da0ee84..6661b28c2fc 100644 --- a/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs +++ b/src/Nethermind/Nethermind.Overseer.Test/Framework/CliqueContext.cs @@ -2,37 +2,31 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Overseer.Test.JsonRpc; -namespace Nethermind.Overseer.Test.Framework +namespace Nethermind.Overseer.Test.Framework; + +public class CliqueContext(CliqueState state) : TestContextBase(state) { - public class CliqueContext : TestContextBase + public CliqueContext Propose(Address address, bool vote) { - public CliqueContext(CliqueState state) : base(state) - { - } - - public CliqueContext Propose(Address address, bool vote) - { - IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; - return AddJsonRpc($"vote {vote} for {address}", "clique_propose", - () => client.PostAsync("clique_propose", new object[] { address, vote })); - } + IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; + return AddJsonRpc($"vote {vote} for {address}", "clique_propose", + () => client.PostAsync("clique_propose", [address, vote])); + } - public CliqueContext Discard(Address address) - { - IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; - return AddJsonRpc($"discard vote for {address}", "clique_discard", - () => client.PostAsync("clique_discard", new object[] { address })); - } + public CliqueContext Discard(Address address) + { + IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; + return AddJsonRpc($"discard vote for {address}", "clique_discard", + () => client.PostAsync("clique_discard", [address])); + } - public CliqueContext SendTransaction(TransactionForRpc tx) - { - IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; - return AddJsonRpc($"send tx to {TestBuilder.CurrentNode.HttpPort}", "eth_sendTransaction", - () => client.PostAsync("eth_SendTransaction", new object[] { tx })); - } + public CliqueContext SendTransaction(TransactionForRpc tx) + { + IJsonRpcClient client = TestBuilder.CurrentNode.JsonRpcClient; + return AddJsonRpc($"send tx to {TestBuilder.CurrentNode.HttpPort}", "eth_sendTransaction", + () => client.PostAsync("eth_SendTransaction", [tx])); } } diff --git a/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs b/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs index 2d097c4a769..9d17ac28787 100644 --- a/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs +++ b/src/Nethermind/Nethermind.Runner/Ethereum/Api/ApiBuilder.cs @@ -11,12 +11,10 @@ using Nethermind.Config; using Nethermind.Consensus; using Nethermind.Core; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Data; +using Nethermind.Facade.Eth.RpcTransaction; using Nethermind.Logging; using Nethermind.Serialization.Json; using Nethermind.Specs.ChainSpecStyle; -using ILogger = Nethermind.Logging.InterfaceLogger; namespace Nethermind.Runner.Ethereum.Api { @@ -83,7 +81,6 @@ private ChainSpec LoadChainSpec(IJsonSerializer ethereumJsonSerializer) IChainSpecLoader loader = new ChainSpecLoader(ethereumJsonSerializer); ChainSpec chainSpec = loader.LoadEmbeddedOrFromFile(chainSpecFile, _logger); - TransactionForRpc.DefaultChainId = chainSpec.ChainId; return chainSpec; }