diff --git a/src/Nethermind/Nethermind.Core.Test/Json/NullableTxTypeConverterTests.cs b/src/Nethermind/Nethermind.Core.Test/Json/NullableTxTypeConverterTests.cs new file mode 100644 index 00000000000..fb1ebab3ce9 --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Json/NullableTxTypeConverterTests.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using Nethermind.Serialization.Json; +using NUnit.Framework; + +namespace Nethermind.Core.Test.Json +{ + [TestFixture] + public class NullableTxTypeConverterTests : ConverterTestBase + { + [TestCase(null)] + [TestCase((TxType)0)] + [TestCase((TxType)15)] + [TestCase((TxType)16)] + [TestCase((TxType)255)] + [TestCase(TxType.Legacy)] + [TestCase(TxType.AccessList)] + [TestCase(TxType.EIP1559)] + public void Test_roundtrip(TxType? arg) + { + TestConverter(arg, (before, after) => before.Equals(after), new NullableTxTypeConverter()); + } + } +} diff --git a/src/Nethermind/Nethermind.Core.Test/Json/TxTypeConverterTests.cs b/src/Nethermind/Nethermind.Core.Test/Json/TxTypeConverterTests.cs index e8591b04d4b..5472fd95e7d 100644 --- a/src/Nethermind/Nethermind.Core.Test/Json/TxTypeConverterTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Json/TxTypeConverterTests.cs @@ -30,6 +30,7 @@ public class TxTypeConverterTests : ConverterTestBase [TestCase((TxType)255)] [TestCase(TxType.Legacy)] [TestCase(TxType.AccessList)] + [TestCase(TxType.EIP1559)] public void Test_roundtrip(TxType arg) { TestConverter(arg, (before, after) => before.Equals(after), new TxTypeConverter()); diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index a4ee12a276f..ef0aed1016f 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -67,7 +67,17 @@ private enum ExecutionOptions /// /// Commit and later restore state also skip validation, use for CallAndRestore /// - CommitAndRestore = Commit | Restore | NoValidation + CommitAndRestore = Commit | Restore | NoValidation, + + /// + /// Zero Gas price + /// + SkipGasPricingValidation = 8, + + /// + /// Commit and restore with zero gas price + /// + CommitAndRestoreWithSkippingGasPricingValidation = CommitAndRestore | SkipGasPricingValidation } public TransactionProcessor( @@ -95,7 +105,14 @@ public TransactionProcessor( public void CallAndRestore(Transaction transaction, BlockHeader block, ITxTracer txTracer) { - Execute(transaction, block, txTracer, ExecutionOptions.CommitAndRestore); + bool skipGasPricing = _specProvider.GetSpec(block.Number).IsEip1559Enabled + ? (transaction.IsEip1559 + ? (transaction.MaxFeePerGas.IsZero && transaction.MaxPriorityFeePerGas.IsZero) + : transaction.GasPrice.IsZero) + : transaction.GasPrice.IsZero; + + Execute(transaction, block, txTracer, + skipGasPricing ? ExecutionOptions.CommitAndRestoreWithSkippingGasPricingValidation : ExecutionOptions.CommitAndRestore); } public void BuildUp(Transaction transaction, BlockHeader block, ITxTracer txTracer) @@ -116,25 +133,51 @@ public void Trace(Transaction transaction, BlockHeader block, ITxTracer txTracer Execute(transaction, block, txTracer, ExecutionOptions.NoValidation); } - private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, bool eip658NotEnabled, - string? reason) + private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, bool eip658NotEnabled, string? reason, bool deleteCallerAccount = false) { - block.GasUsed += tx.GasLimit; - - Address recipient = tx.To ?? ContractAddress.From( - tx.SenderAddress ?? Address.Zero, - _stateProvider.GetNonce(tx.SenderAddress ?? Address.Zero)); + try + { + // block.GasUsed += tx.GasLimit; + // + // Address recipient = tx.To ?? ContractAddress.From( + // tx.SenderAddress ?? Address.Zero, + // _stateProvider.GetNonce(tx.SenderAddress ?? Address.Zero)); + // + // if (txTracer.IsTracingReceipt) + // { + // Keccak? stateRoot = null; + // if (eip658NotEnabled) + // { + // _stateProvider.RecalculateStateRoot(); + // stateRoot = _stateProvider.StateRoot; + // } + // + // txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty(), reason ?? "invalid", stateRoot); + + block.GasUsed += tx.GasLimit; + + Address recipient = tx.To ?? ContractAddress.From( + tx.SenderAddress ?? Address.Zero, + _stateProvider.GetNonce(tx.SenderAddress ?? Address.Zero)); + + if (txTracer.IsTracingReceipt) + { + Keccak? stateRoot = null; + if (eip658NotEnabled) + { + _stateProvider.RecalculateStateRoot(); + stateRoot = _stateProvider.StateRoot; + } - if (txTracer.IsTracingReceipt) + txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty(), reason ?? "invalid", stateRoot); + } + } + finally { - Keccak? stateRoot = null; - if (eip658NotEnabled) + if (deleteCallerAccount) { - _stateProvider.RecalculateStateRoot(); - stateRoot = _stateProvider.StateRoot; + _stateProvider.DeleteAccount(tx.SenderAddress ?? throw new InvalidOperationException()); } - - txTracer.MarkAsFailed(recipient, tx.GasLimit, Array.Empty(), reason ?? "invalid", stateRoot); } } @@ -144,12 +187,16 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra bool eip658NotEnabled = !_specProvider.GetSpec(block.Number).IsEip658Enabled; // restore is CallAndRestore - previous call, we will restore state after the execution + //bool restore = (executionOptions & ExecutionOptions.Restore) != ExecutionOptions.None; bool restore = (executionOptions & ExecutionOptions.Restore) == ExecutionOptions.Restore; bool noValidation = (executionOptions & ExecutionOptions.NoValidation) == ExecutionOptions.NoValidation; // commit - is for standard execute, we will commit thee state after execution bool commit = (executionOptions & ExecutionOptions.Commit) == ExecutionOptions.Commit || eip658NotEnabled; //!commit - is for build up during block production, we won't commit state after each transaction to support rollbacks - //we commit only after all block is constructed + //we commit only after all block is constructed + + bool skipGasPricing = (executionOptions & ExecutionOptions.SkipGasPricingValidation) == ExecutionOptions.SkipGasPricingValidation; + bool notSystemTransaction = !transaction.IsSystem(); bool deleteCallerAccount = false; @@ -161,10 +208,26 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra UInt256 value = transaction.Value; - if (!transaction.TryCalculatePremiumPerGas(block.BaseFeePerGas, out UInt256 premiumPerGas) && !noValidation) + // if (!transaction.TryCalculatePremiumPerGas(block.BaseFeePerGas, out UInt256 premiumPerGas) && !noValidation) + // { + // TraceLogInvalidTx(transaction, "MINER_PREMIUM_IS_NEGATIVE"); + // QuickFail(transaction, block, txTracer, eip658NotEnabled, "miner premium is negative"); + // return; + // } + if (!skipGasPricing && restore && transaction.MaxPriorityFeePerGas > transaction.MaxFeePerGas) + { + TraceLogInvalidTx(transaction, "MAX PRIORITY FEE PER GAS HIGHER THAN MAX FEE PER GAS"); + QuickFail(transaction, block, txTracer, eip658NotEnabled, + $"max priority fee per gas higher than max fee per gas: address {transaction.SenderAddress}, maxPriorityFeePerGas: {transaction.MaxPriorityFeePerGas}, maxFeePerGas: {transaction.MaxFeePerGas}"); + return; + } + + if (!transaction.TryCalculatePremiumPerGas(block.BaseFeePerGas, out UInt256 premiumPerGas) && !skipGasPricing && !restore) { - TraceLogInvalidTx(transaction, "MINER_PREMIUM_IS_NEGATIVE"); - QuickFail(transaction, block, txTracer, eip658NotEnabled, "miner premium is negative"); + // max fee per gas (feeCap) less than block base fee + TraceLogInvalidTx(transaction, "MAX FEE PER GAS LESS THAN BLOCK BASE FEE"); + QuickFail(transaction, block, txTracer, eip658NotEnabled, + $"max fee per gas less than block base fee: address {transaction.SenderAddress}, maxFeePerGas: {transaction.MaxFeePerGas}, baseFee {block.BaseFeePerGas}"); return; } @@ -200,15 +263,16 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra if (gasLimit < intrinsicGas) { TraceLogInvalidTx(transaction, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}"); - QuickFail(transaction, block, txTracer, eip658NotEnabled, "gas limit below intrinsic gas"); + QuickFail(transaction, block, txTracer, eip658NotEnabled, $"gas limit below intrinsic gas: have {gasLimit}, want {intrinsicGas}"); return; } + // if (!restore && gasLimit > block.GasLimit - block.GasUsed) if (!noValidation && gasLimit > block.GasLimit - block.GasUsed) { TraceLogInvalidTx(transaction, $"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}"); - QuickFail(transaction, block, txTracer, eip658NotEnabled, "block gas limit exceeded"); + QuickFail(transaction, block, txTracer, eip658NotEnabled, $"block gas limit exceeded: gasLimit {gasLimit} > block gasLimit {block.GasLimit} - block gasUsed {block.GasUsed}"); return; } } @@ -231,6 +295,7 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra else { TraceLogInvalidTx(transaction, $"SENDER_ACCOUNT_DOES_NOT_EXIST {caller}"); + // if (!commit || restore || effectiveGasPrice == UInt256.Zero) if (!commit || noValidation || effectiveGasPrice == UInt256.Zero) { deleteCallerAccount = !commit || restore; @@ -244,36 +309,45 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra $"Failed to recover sender address on tx {transaction.Hash} when previously recovered sender account did not exist."); } } + UInt256 senderReservedGasPayment = skipGasPricing ? UInt256.Zero : (ulong) gasLimit * effectiveGasPrice; - UInt256 senderReservedGasPayment = noValidation ? UInt256.Zero : (ulong)gasLimit * effectiveGasPrice; + // UInt256 senderReservedGasPayment = noValidation ? UInt256.Zero : (ulong)gasLimit * effectiveGasPrice; if (notSystemTransaction) { UInt256 senderBalance = _stateProvider.GetBalance(caller); - if (!noValidation && ((ulong)intrinsicGas * effectiveGasPrice + value > senderBalance || - senderReservedGasPayment + value > senderBalance)) + // if (!noValidation && ((ulong)intrinsicGas * effectiveGasPrice + value > senderBalance || + // senderReservedGasPayment + value > senderBalance)) + if (!skipGasPricing && ((ulong) intrinsicGas * effectiveGasPrice + value > senderBalance || senderReservedGasPayment + value > senderBalance)) { - TraceLogInvalidTx(transaction, - $"INSUFFICIENT_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}"); - QuickFail(transaction, block, txTracer, eip658NotEnabled, "insufficient sender balance"); + // TraceLogInvalidTx(transaction, + // $"INSUFFICIENT_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}"); + // QuickFail(transaction, block, txTracer, eip658NotEnabled, "insufficient sender balance"); + TraceLogInvalidTx(transaction, $"INSUFFICIENT_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}"); + QuickFail(transaction, block, txTracer, eip658NotEnabled, $"insufficient funds for gas * price + value: address {caller}, have {senderBalance}, want {senderReservedGasPayment + value}", deleteCallerAccount && restore); return; } - if (!noValidation && spec.IsEip1559Enabled && !transaction.IsFree() && - senderBalance < (UInt256)transaction.GasLimit * transaction.MaxFeePerGas + value) + // if (!noValidation && spec.IsEip1559Enabled && !transaction.IsFree() && + // senderBalance < (UInt256)transaction.GasLimit * transaction.MaxFeePerGas + value) + if (!skipGasPricing && spec.IsEip1559Enabled && !transaction.IsServiceTransaction && senderBalance < (UInt256)transaction.GasLimit * transaction.MaxFeePerGas + value) { - TraceLogInvalidTx(transaction, - $"INSUFFICIENT_MAX_FEE_PER_GAS_FOR_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}, MAX_FEE_PER_GAS: {transaction.MaxFeePerGas}"); - QuickFail(transaction, block, txTracer, eip658NotEnabled, - "insufficient MaxFeePerGas for sender balance"); + // TraceLogInvalidTx(transaction, + // $"INSUFFICIENT_MAX_FEE_PER_GAS_FOR_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}, MAX_FEE_PER_GAS: {transaction.MaxFeePerGas}"); + // QuickFail(transaction, block, txTracer, eip658NotEnabled, + // "insufficient MaxFeePerGas for sender balance"); + TraceLogInvalidTx(transaction, $"INSUFFICIENT_MAX_FEE_PER_GAS_FOR_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}, MAX_FEE_PER_GAS: {transaction.MaxFeePerGas}"); + QuickFail(transaction, block, txTracer, eip658NotEnabled, $"insufficient MaxFeePerGas for sender balance: address {{caller}}, sender_balance = {senderBalance}, maxFeePerGas: {transaction.MaxFeePerGas}", deleteCallerAccount && restore); return; } if (transaction.Nonce != _stateProvider.GetNonce(caller)) { - TraceLogInvalidTx(transaction, - $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(caller)})"); - QuickFail(transaction, block, txTracer, eip658NotEnabled, "wrong transaction nonce"); + // TraceLogInvalidTx(transaction, + // $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(caller)})"); + // QuickFail(transaction, block, txTracer, eip658NotEnabled, "wrong transaction nonce"); + TraceLogInvalidTx(transaction, $"WRONG_TRANSACTION_NONCE: {transaction.Nonce} (expected {_stateProvider.GetNonce(caller)})"); + QuickFail(transaction, block, txTracer, eip658NotEnabled, "wrong transaction nonce", deleteCallerAccount && restore); return; } @@ -358,7 +432,7 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra } else { - // tks: there is similar code fo contract creation from init and from CREATE + // tks: there is similar code for contract creation from init and from CREATE // this may lead to inconsistencies (however it is tested extensively in blockchain tests) if (transaction.IsContractCreation) { diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs index 45d3ff80df7..0fa47781946 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Data/Eip2930Tests.cs @@ -180,8 +180,8 @@ public void can_deserialize_not_provided_txType() { _transactionForRpc = _serializer.Deserialize("{\"nonce\":\"0x0\",\"blockHash\":null,\"blockNumber\":null,\"transactionIndex\":null,\"to\":null,\"value\":\"0x0\",\"gasPrice\":\"0x0\",\"gas\":\"0x0\",\"input\":null}"); - // 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 null + _transactionForRpc.Type.Should().Be(null); } [Test] @@ -189,8 +189,8 @@ public void can_deserialize_direct_null_txType() { _transactionForRpc = _serializer.Deserialize("{\"nonce\":\"0x0\",\"blockHash\":null,\"blockNumber\":null,\"transactionIndex\":null,\"to\":null,\"value\":\"0x0\",\"gasPrice\":\"0x0\",\"gas\":\"0x0\",\"input\":null,\"type\":null}"); - // if there is null TxType provided, still default value should be TxType.Legacy equal 0 - _transactionForRpc.Type.Should().Be(0); + // if there is null TxType provided, still default value should be null + _transactionForRpc.Type.Should().Be(null); } [Test] 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 382553fc325..8c4b4bb9247 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EstimateGas.cs @@ -36,13 +36,35 @@ namespace Nethermind.JsonRpc.Test.Modules.Eth public partial class EthRpcModuleTests { [Test] - public async Task Eth_estimateGas_web3_should_return_insufficient_balance_error() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_estimateGas_web3_should_return_insufficient_balance_error(string type) { using Context ctx = await Context.Create(); Address someAccount = new("0x0001020304050607080910111213141516171819"); ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); + "{" + type + "\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); + string serialized = + ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"insufficient funds for gas * price + value: address 0x0001020304050607080910111213141516171819, have 0, want 26214400000500\"},\"id\":67}", + serialized); + ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); + } + + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_estimateGas_web3_without_gas_pricing(string type) + { + using Context ctx = await Context.Create(); + Address someAccount = new("0x0001020304050607080910111213141516171819"); + ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\":\"0x0001020304050607080910111213141516171819\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); string serialized = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual( @@ -50,46 +72,83 @@ public async Task Eth_estimateGas_web3_should_return_insufficient_balance_error( serialized); ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } + + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_estimateGas_web3_without_gas_pricing_and_value(string type) + { + using Context ctx = await Context.Create(); + Address someAccount = new("0x0001020304050607080910111213141516171819"); + ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\":\"0x0001020304050607080910111213141516171819\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + string serialized = + ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}", + serialized); + ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); + } [Test] - public async Task Eth_estimateGas_web3_sample_not_enough_gas_system_account() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_estimateGas_web3_sample_not_enough_gas_system_account(string type) { using Context ctx = await Context.Create(); ctx._test.ReadOnlyState.AccountExists(Address.SystemUser).Should().BeFalse(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + "{" + type + "\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); - Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}", serialized); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient funds for transfer: address 0xfffffffffffffffffffffffffffffffffffffffe\"},\"id\":67}", + serialized); + ctx._test.ReadOnlyState.AccountExists(Address.SystemUser).Should().BeFalse(); } [Test] - public async Task Eth_estimateGas_web3_sample_not_enough_gas_other_account() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_estimateGas_web3_sample_not_enough_gas_other_account(string type) { using Context ctx = await Context.Create(); Address someAccount = new("0x0001020304050607080910111213141516171819"); ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + "{" + type + "\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); - Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}", serialized); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"insufficient funds for gas * price + value: address 0x0001020304050607080910111213141516171819, have 0, want 26214400000000\"},\"id\":67}", + serialized); + ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } [Test] - public async Task Eth_estimateGas_web3_above_block_gas_limit() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_estimateGas_web3_below_intrinsic_gas_limit(string type) { using Context ctx = await Context.Create(); Address someAccount = new("0x0001020304050607080910111213141516171819"); ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gas\":\"0x100000000\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + "{" + type + "\"from\":\"0x0001020304050607080910111213141516171819\",\"gas\":\"0x100\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); - Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x53b8\",\"id\":67}", serialized); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"gas limit below intrinsic gas: have 256, want 21432\"},\"id\":67}", + serialized); + ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } @@ -173,52 +232,66 @@ public async Task Eth_estimate_gas_is_lower_with_optimized_access_list() } [Test] - public async Task Estimate_gas_without_gas_pricing() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Estimate_gas_without_gas_pricing(string type) { using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + "{" + type + "\"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}", serialized); } [Test] - public async Task Estimate_gas_with_gas_pricing() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Estimate_gas_with_gas_pricing(string type) { using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); - string serialized = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); - Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}", serialized); + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); + string serialized = + ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); + + Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"insufficient funds for gas * price + value: address 0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24, have 0, want 400000000\"},\"id\":67}", serialized); } [Test] - public async Task Estimate_gas_without_gas_pricing_after_1559_legacy() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Estimate_gas_without_gas_pricing_after_1559_legacy(string type) { using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\"}"); string serialized = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}", serialized); + } [Test] - public async Task Estimate_gas_without_gas_pricing_after_1559_new_type_of_transaction() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Estimate_gas_without_gas_pricing_after_1559_new_type_of_transaction(string type) { using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\"}"); + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\"}"); string serialized = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}", serialized); - byte[] code = Prepare.EvmCode - .Op(Instruction.BASEFEE) - .PushData(0) - .Op(Instruction.SSTORE) - .Done; } [Test] - public async Task Estimate_gas_with_base_fee_opcode() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Estimate_gas_with_base_fee_opcode(string type) { using Context ctx = await Context.CreateWithLondonEnabled(); @@ -233,8 +306,10 @@ 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 = ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); + $"{{{type}\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"data\": \"{dataStr}\"}}"); + string serialized = + ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( "{\"jsonrpc\":\"2.0\",\"result\":\"0xe891\",\"id\":67}", serialized); @@ -260,6 +335,7 @@ public async Task Estimate_gas_with_revert() serialized); } + [Test] public async Task should_estimate_transaction_with_deployed_code_when_eip3607_enabled() { @@ -276,5 +352,6 @@ public async Task should_estimate_transaction_with_deployed_code_when_eip3607_en ctx._test.TestEthRpc("eth_estimateGas", ctx._test.JsonSerializer.Serialize(transaction), "latest"); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x5208\",\"id\":67}", serialized); } + } } 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 6cb72dfb156..893438612ba 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.EthCall.cs @@ -33,55 +33,72 @@ namespace Nethermind.JsonRpc.Test.Modules.Eth public partial class EthRpcModuleTests { [Test] - public async Task Eth_call_web3_sample() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_call_web3_sample(string type) { using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + "{" + type + "\"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction), "0x0"); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}", serialized); } [Test] - public async Task Eth_call_web3_sample_not_enough_gas_system_account() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_call_web3_sample_not_enough_gas_system_account(string type) { using Context ctx = await Context.Create(); ctx._test.ReadOnlyState.AccountExists(Address.SystemUser).Should().BeFalse(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + "{" + type + "\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction), "0x0"); - Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}", serialized); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient funds for transfer: address 0xfffffffffffffffffffffffffffffffffffffffe\"},\"id\":67}", + serialized); ctx._test.ReadOnlyState.AccountExists(Address.SystemUser).Should().BeFalse(); + } [Test] - public async Task Eth_call_web3_should_return_insufficient_balance_error() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_call_web3_should_return_insufficient_balance_error(string type) { using Context ctx = await Context.Create(); Address someAccount = new("0x0001020304050607080910111213141516171819"); ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); + "{" + type + "\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"value\": 500}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual( - "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"insufficient funds for transfer: address 0x0001020304050607080910111213141516171819\"},\"id\":67}", + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"insufficient funds for gas * price + value: address 0x0001020304050607080910111213141516171819, have 0, want 26214400000500\"},\"id\":67}", serialized); ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } [Test] - public async Task Eth_call_web3_sample_not_enough_gas_other_account() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_call_web3_sample_not_enough_gas_other_account(string type) { using Context ctx = await Context.Create(); Address someAccount = new("0x0001020304050607080910111213141516171819"); ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + "{" + type + "\"from\":\"0x0001020304050607080910111213141516171819\",\"gasPrice\":\"0x100000\", \"data\": \"0x70a082310000000000000000000000006c1f09f6271fbe133db38db9c9280307f5d22160\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction), "0x0"); - Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}", serialized); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"insufficient funds for gas * price + value: address 0x0001020304050607080910111213141516171819, have 0, want 26214400000000\"},\"id\":67}", + serialized); ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); } @@ -103,7 +120,7 @@ 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.Data = new byte[] {1, 2, 3}; + transaction.Data = new byte[] { 1, 2, 3 }; string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction), "latest"); @@ -112,7 +129,6 @@ public async Task Eth_call_no_recipient_should_work_as_init() serialized); } - [Test] public async Task should_not_reject_transactions_with_deployed_code_when_eip3607_enabled() { @@ -186,53 +202,199 @@ public async Task Eth_call_with_accessList() Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x010203\",\"id\":67}", serialized); } + /// + /// 1. Before 1559: If you do not specify the gas price, 0 is used and you can call with accounts with 0 balance. + /// [Test] - public async Task Eth_call_without_gas_pricing() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_call_without_gas_pricing(string type) { using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + "{" + type + "\"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}", serialized); } + /// + /// 2. Before 1559: If you specify the gas price, it is used and checked with the account balance. + /// + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_call_with_gas_pricing(string type) + { + using Context ctx = await Context.Create(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"insufficient funds for gas * price + value: address 0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24, have 0, want 400000000\"},\"id\":67}", + serialized); + } + + /// + /// 3. Before 1559: If you specify a gas price of type 1559, it will be ignored and the gas price will be 0. + /// [Test] - public async Task Eth_call_with_gas_pricing() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x0\", ")] + public async Task Eth_call_with_1559_gas_pricing(string type) { using Context ctx = await Context.Create(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"maxFeePerGas\": \"0x100\"}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}", serialized); } [Test] - public async Task Eth_call_without_gas_pricing_after_1559_legacy() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_without_gas_pricing_after_1559_legacy(string type) { using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gasPrice\": \"0x10\"}"); + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\"}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}", serialized); } + /// + /// 4. After 1559: If you do not specify the gas price, 0 is used and you can call with accounts with 0 balance. The basefee will be 0. + /// [Test] - public async Task Eth_call_without_gas_pricing_after_1559_new_type_of_transaction() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_without_gas_pricing_after_1559_new_type_of_transaction(string type) { using Context ctx = await Context.CreateWithLondonEnabled(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - "{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\"}"); + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\"}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}", serialized); - byte[] code = Prepare.EvmCode - .Op(Instruction.BASEFEE) - .PushData(0) - .Op(Instruction.SSTORE) - .Done; + } + + /// + /// 5. After 1559: In geth if you do specify a gas price, that is interpreted as both the max and priority fee + /// and your account balance is checked against them + the current base fee. + /// + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + public async Task Eth_call_with_gas_pricing_after_1559_new_type_of_transaction(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"gasPrice\": \"0x100000000\", \"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"insufficient funds for gas * price + value: address 0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24, have 0, want 107374182400000000\"},\"id\":67}", + serialized); + } + + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_with_gas_limit_after_1559_new_type_of_transaction(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"gas\": \"0x1000\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"gas limit below intrinsic gas: have 4096, want 21000\"},\"id\":67}", + serialized); + } + + /// + /// 6. After 1559: If you specify a gas price of type 1559 and transaction type 0x2, + /// the transaction will be of type 1559 and your account balance is checked against them + the current basefee. + /// (maxFeePerGas) + /// + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_with_max_fee_after_1559_new_type_of_transaction(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"maxFeePerGas\": \"0x2DA2830C\", \"value\": \"0x2DA2830C\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"insufficient funds for gas * price + value: address 0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24, have 0, want 19140625765625100\"},\"id\":67}", + serialized); + } + + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_with_max_fee_after_1559_new_type_of_transaction_success(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + await ctx._test.AddFundsAfterLondon((new Address("0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24"), 2.Ether())); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"maxPriorityFeePerGas\": \"0x2DA2830C\", \"maxFeePerGas\": \"0x2DA2830C\", \"value\": \"0x2DA2830C\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual(2.Ether(), + ctx._test.ReadOnlyState.GetBalance(new Address("0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24"))); + Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":\"0x\",\"id\":67}", serialized); + } + + /// + /// 6. After 1559: If you specify a gas price of type 1559 and transaction type 0x2, + /// the transaction will be of type 1559 and your account balance is checked against them + the current basefee. + /// (maxFeePerGas) + /// + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_with_max__priority_fee_after_1559_new_type_of_transaction(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"maxFeePerGas\": \"0x2d5dd655ddD\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"insufficient funds for gas * price + value: address 0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24, have 0, want 19140625000000000\"},\"id\":67}", + serialized); } [Test] - public async Task Eth_call_with_base_fee_opcode() + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x1\", ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_with_dissimilar_account_nonce(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + Address someAccount = new Address("0x0d8775f648430679a709e98d2b0cb6250d2887ef"); + ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"nonce\": \"105\", \"from\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\", \"to\": \"0x0d8775f648430679a709e98d2b0cb6250d2887ef\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"wrong transaction nonce\"},\"id\":67}", + serialized); + ctx._test.ReadOnlyState.AccountExists(someAccount).Should().BeFalse(); + } + + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x1\", ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_with_base_fee_opcode(string type) { using Context ctx = await Context.CreateWithLondonEnabled(); @@ -247,12 +409,59 @@ public async Task Eth_call_with_base_fee_opcode() string dataStr = code.ToHexString(); TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( - $"{{\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"type\": \"0x2\", \"data\": \"{dataStr}\"}}"); + $"{{{type}\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"data\": \"{dataStr}\"}}"); string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); Assert.AreEqual( "{\"jsonrpc\":\"2.0\",\"result\":\"0x000000000000000000000000000000000000000000000000000000002da282a8\",\"id\":67}", serialized); } + + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x1\", ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_for_checking_invalid_input(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"maxPriorityFeePerGas\": \"0x2DA2830C\", \"gasPrice\": \"0x2DA2830C\",}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified\"},\"id\":67}", + serialized); + } + + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x1\", ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_for_checking_invalid_input_2(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"maxFeePerGas\": \"0x2DA2830C\", \"gasPrice\": \"0x2DA2830C\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32000,\"message\":\"both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified\"},\"id\":67}", + serialized); + } + + [Test] + [TestCase("")] + [TestCase("\"type\":null, ")] + [TestCase("\"type\":\"0x2\", ")] + public async Task Eth_call_maxFeePerGas_less_than_block_baseFee(string type) + { + using Context ctx = await Context.CreateWithLondonEnabled(); + TransactionForRpc transaction = ctx._test.JsonSerializer.Deserialize( + "{" + type + "\"from\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"to\": \"0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24\", \"maxFeePerGas\": \"0x2DA2830\"}"); + string serialized = ctx._test.TestEthRpc("eth_call", ctx._test.JsonSerializer.Serialize(transaction)); + Assert.AreEqual( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32015,\"message\":\"VM execution error.\",\"data\":\"max fee per gas less than block base fee: address 0x32e4e4c7c5d1cea5db5f9202a9e4d99e56c91a24, maxFeePerGas: 47851568, baseFee 765625000\"},\"id\":67}", + serialized); + } [Test] public async Task Eth_call_with_revert() diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs index d8dc5894a67..810880b3b71 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs @@ -619,7 +619,7 @@ public async Task Trace_call_simple_tx_test() string transaction = "{\"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}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0xaaaaaaaa8583de65cc752fe3fad5098643244d22\",\"gas\":\"0x17d2638\",\"input\":\"0x\",\"to\":\"0xd6a8d04cb9846759416457e2c593c99390092df6\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"vmTrace\":null},\"id\":67}"; string serialized = RpcTest.TestSerializedRequest( EthModuleFactory.Converters.Union(TraceModuleFactory.Converters).ToList(), context.TraceRpcModule, @@ -628,8 +628,8 @@ public async Task Trace_call_simple_tx_test() Assert.AreEqual(expectedResult, serialized, 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}")] + //[TestCase("{\"from\":\"0x7f554713be84160fdf0178cc8df86f5aabd33397\",\"to\":\"0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f\",\"value\":\"0x0\",\"gasPrice\":\"0x482908C\",\"data\":\"0x\"}", "[\"trace\"]","{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x7f554713be84160fdf0178cc8df86f5aabd33397\",\"gas\":\"0x17d7840\",\"input\":\"0x\",\"to\":\"0xbe5c953dd0ddb0ce033a98f36c981f1b74d3b33f\",\"value\":\"0x0\"},\"error\":\"insufficient funds for gas * price + value: address 0x7f554713be84160fdf0178cc8df86f5aabd33397, have 0, want 1891638707450000000\",\"subtraces\":0,\"traceAddress\":null,\"type\":null}],\"vmTrace\":null},\"id\":67}")] + [TestCase("{\"from\":\"0x38cd7db12edc7724a6a403c1a63d3c12682fd687\",\"to\":\"0xe4d75e9b493458d032a5c3cc1ee9b0712c1ece06\",\"gasPrice\":\"0x1512386900\",\"data\":\"0x\"}", "[\"trace\"]", "{\"jsonrpc\":\"2.0\",\"result\":{\"output\":\"0x\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0xc71acc7863f3bc7347b24c3b835643bd89d4d161\",\"gas\":\"0x17d7840\",\"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) { Context context = new(); diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpc.cs b/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpc.cs index f430214fa35..558fe9f7081 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpc.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpc.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using MathGmp.Native; using Nethermind.Core; @@ -103,7 +104,8 @@ public TransactionForRpc() public UInt256? ChainId { get; set; } - public TxType Type { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Include)] + public TxType? Type { get; set; } public AccessListItemForRpc[]? AccessList { get; set; } @@ -129,7 +131,7 @@ public TransactionForRpc() SenderAddress = From, Value = Value ?? 0, Data = Data ?? Input, - Type = Type, + Type = Type ?? TxType.Legacy, AccessList = TryGetAccessList(), ChainId = chainId, DecodedMaxFeePerGas = MaxFeePerGas ?? 0 @@ -156,7 +158,7 @@ public TransactionForRpc() SenderAddress = From, Value = Value ?? 0, Data = Data ?? Input, - Type = Type, + Type = Type ?? TxType.Legacy, AccessList = TryGetAccessList(), ChainId = chainId }; diff --git a/src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs b/src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs index f17df8e43d3..d6a895ca7a6 100644 --- a/src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs +++ b/src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs @@ -72,7 +72,7 @@ public interface IJsonRpcConfig : IConfig [ConfigItem( Description = "Gas limit for eth_call and eth_estimateGas", - DefaultValue = "100000000")] + DefaultValue = "25000000")] long? GasCap { get; set; } [ConfigItem( diff --git a/src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs b/src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs index 603e4ffa6ed..e6361bd674b 100644 --- a/src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs +++ b/src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs @@ -42,8 +42,9 @@ public int WebSocketsPort public string? IpcUnixDomainSocketPath { get; set; } = null; public string[] EnabledModules { get; set; } = ModuleType.DefaultModules.ToArray(); + public int FindLogBlockDepthLimit { get; set; } = 1000; + public long? GasCap { get; set; } = 25000000; public string[] AdditionalRPCUrls { get; set; } = Array.Empty(); - public long? GasCap { get; set; } = 100000000; public int ReportIntervalSeconds { get; set; } = 300; public bool BufferResponses { get; set; } public string CallsFilterFilePath { get; set; } = "Data/jsonrpc.filter"; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthModule.TransactionExecutor.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthModule.TransactionExecutor.cs index 76351588368..1af736ca29a 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthModule.TransactionExecutor.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthModule.TransactionExecutor.cs @@ -21,10 +21,12 @@ using Nethermind.Core; using Nethermind.Core.Eip2930; using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; using Nethermind.Evm; using Nethermind.Facade; using Nethermind.Int256; using Nethermind.JsonRpc.Data; +using Nethermind.Specs; using Nethermind.Specs.Forks; namespace Nethermind.JsonRpc.Modules.Eth @@ -36,12 +38,14 @@ private abstract class TxExecutor protected readonly IBlockchainBridge _blockchainBridge; private readonly IBlockFinder _blockFinder; private readonly IJsonRpcConfig _rpcConfig; + private readonly ISpecProvider _specProvider; - protected TxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig) + protected TxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig, ISpecProvider specProvider) { _blockchainBridge = blockchainBridge; _blockFinder = blockFinder; _rpcConfig = rpcConfig; + _specProvider = specProvider; } public ResultWrapper ExecuteTx( @@ -53,7 +57,6 @@ public ResultWrapper ExecuteTx( { return ResultWrapper.Fail(searchResult); } - BlockHeader header = searchResult.Object; if (!HasStateForBlock(_blockchainBridge, header)) { @@ -63,6 +66,19 @@ public ResultWrapper ExecuteTx( transactionCall.EnsureDefaults(_rpcConfig.GasCap); + if ((transactionCall.MaxFeePerGas != null || transactionCall.MaxPriorityFeePerGas != null) && + transactionCall.GasPrice != null) + { + return ResultWrapper.Fail("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified", ErrorCodes.InvalidInput); + } + + bool isEip1559Enabled = _specProvider.GetSpec(header.Number).IsEip1559Enabled; + + transactionCall.Type ??= (isEip1559Enabled && + (transactionCall.MaxFeePerGas != null || transactionCall.MaxPriorityFeePerGas != null)) + ? TxType.EIP1559 + : TxType.Legacy; + using CancellationTokenSource cancellationTokenSource = new(_rpcConfig.Timeout); Transaction tx = transactionCall.ToTransaction(_blockchainBridge.GetChainId()); return ExecuteTx(header, tx, cancellationTokenSource.Token); @@ -76,8 +92,8 @@ protected ResultWrapper GetInputError(BlockchainBridge.CallOutput resul private class CallTxExecutor : TxExecutor { - public CallTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig) - : base(blockchainBridge, blockFinder, rpcConfig) + public CallTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig, ISpecProvider specProvider) + : base(blockchainBridge, blockFinder, rpcConfig, specProvider) { } @@ -98,8 +114,8 @@ protected override ResultWrapper ExecuteTx(BlockHeader header, Transacti private class EstimateGasTxExecutor : TxExecutor { - public EstimateGasTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig) - : base(blockchainBridge, blockFinder, rpcConfig) + public EstimateGasTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig, ISpecProvider specProvider) + : base(blockchainBridge, blockFinder, rpcConfig, specProvider) { } @@ -122,8 +138,8 @@ private class CreateAccessListTxExecutor : TxExecutor { private readonly bool _optimize; - public CreateAccessListTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig, bool optimize) - : base(blockchainBridge, blockFinder, rpcConfig) + public CreateAccessListTxExecutor(IBlockchainBridge blockchainBridge, IBlockFinder blockFinder, IJsonRpcConfig rpcConfig, ISpecProvider specProvider, bool optimize) + : base(blockchainBridge, blockFinder, rpcConfig, specProvider) { _optimize = optimize; } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs index 7f2fd49d1a3..cf7811071fb 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs @@ -362,15 +362,15 @@ private async Task> SendTx(Transaction tx, } public ResultWrapper eth_call(TransactionForRpc transactionCall, BlockParameter? blockParameter = null) => - new CallTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig) + new CallTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig, _specProvider) .ExecuteTx(transactionCall, blockParameter); public ResultWrapper eth_estimateGas(TransactionForRpc transactionCall, BlockParameter blockParameter) => - new EstimateGasTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig) + new EstimateGasTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig, _specProvider) .ExecuteTx(transactionCall, blockParameter); public ResultWrapper eth_createAccessList(TransactionForRpc transactionCall, BlockParameter? blockParameter = null, bool optimize = true) => - new CreateAccessListTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig, optimize) + new CreateAccessListTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig, _specProvider, optimize) .ExecuteTx(transactionCall, blockParameter); public ResultWrapper eth_getBlockByHash(Keccak blockHash, bool returnFullTransactionObjects) diff --git a/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs b/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs index b3678e262b2..47fbd95bc8a 100644 --- a/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs +++ b/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs @@ -54,7 +54,8 @@ public EthereumJsonSerializer() new BigIntegerConverter(), new NullableBigIntegerConverter(), new PublicKeyConverter(), - new TxTypeConverter() + new TxTypeConverter(), + new NullableTxTypeConverter() }); public IList BasicConverters { get; } = CommonConverters.ToList(); @@ -74,7 +75,8 @@ public EthereumJsonSerializer() new BigIntegerConverter(NumberConversion.Decimal), new NullableBigIntegerConverter(NumberConversion.Decimal), new PublicKeyConverter(), - new TxTypeConverter() + new TxTypeConverter(), + new NullableTxTypeConverter() }; public T Deserialize(Stream stream) diff --git a/src/Nethermind/Nethermind.Serialization.Json/NullableTxTypeConverter.cs b/src/Nethermind/Nethermind.Serialization.Json/NullableTxTypeConverter.cs new file mode 100644 index 00000000000..c2ed60c0e9c --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Json/NullableTxTypeConverter.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using System; +using Nethermind.Core; +using Newtonsoft.Json; + +namespace Nethermind.Serialization.Json +{ + public class NullableTxTypeConverter : JsonConverter + { + private TxTypeConverter _txTypeConverter; + + public NullableTxTypeConverter() + { + _txTypeConverter = new TxTypeConverter(); + } + + public override void WriteJson(JsonWriter writer, TxType? value, JsonSerializer serializer) + { + if (!value.HasValue) + { + writer.WriteNull(); + return; + } + + _txTypeConverter.WriteJson(writer, value.Value, serializer); + } + + public override TxType? ReadJson(JsonReader reader, Type objectType, TxType? existingValue, bool hasExistingValue, + JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null || reader.Value == null) + { + return null; + } + + return _txTypeConverter.ReadJson(reader, objectType, existingValue ?? TxType.Legacy, hasExistingValue, serializer); + } + } +} diff --git a/src/int256 b/src/int256 index 548d4aee373..ab61bef664d 160000 --- a/src/int256 +++ b/src/int256 @@ -1 +1 @@ -Subproject commit 548d4aee373b48c68609ef1eb909673a76cde715 +Subproject commit ab61bef664d3238cdb6f3aa189501b2684350db2 diff --git a/src/rocksdb-sharp b/src/rocksdb-sharp index 63cc551ecf8..0d6715ddaeb 160000 --- a/src/rocksdb-sharp +++ b/src/rocksdb-sharp @@ -1 +1 @@ -Subproject commit 63cc551ecf8bb1d37dbf9ce0c13ae9673073f235 +Subproject commit 0d6715ddaeb1adca540f31aee2e052957a1652e4