diff --git a/src/Nethermind/Nethermind.Consensus/Tracing/ITracer.cs b/src/Nethermind/Nethermind.Consensus/Tracing/ITracer.cs index 89cb5333fe9..04ff37539e7 100644 --- a/src/Nethermind/Nethermind.Consensus/Tracing/ITracer.cs +++ b/src/Nethermind/Nethermind.Consensus/Tracing/ITracer.cs @@ -14,12 +14,18 @@ namespace Nethermind.Consensus.Tracing public interface ITracer { /// - /// Allows to trace an arbitrarily constructed block. + /// Allows to trace an arbitrarily constructed block. Do NOT subtract gas from sender account /// /// Block to trace. /// Trace to act on block processing events. - /// Processed block - Block? Trace(Block block, IBlockTracer tracer); + void Trace(Block block, IBlockTracer tracer); + + /// + /// Allows to trace and verify arbitrary constructed block. Subtracts gas from sender account + /// + /// Block to trace. + /// Trace to act on block processing events. + void Execute(Block block, IBlockTracer tracer); void Accept(ITreeVisitor visitor, Keccak stateRoot); } diff --git a/src/Nethermind/Nethermind.Consensus/Tracing/Tracer.cs b/src/Nethermind/Nethermind.Consensus/Tracing/Tracer.cs index 0617ef7a073..c81e4f00d94 100644 --- a/src/Nethermind/Nethermind.Consensus/Tracing/Tracer.cs +++ b/src/Nethermind/Nethermind.Consensus/Tracing/Tracer.cs @@ -14,27 +14,29 @@ namespace Nethermind.Consensus.Tracing public class Tracer : ITracer { private readonly IWorldState _stateProvider; - private readonly IBlockchainProcessor _blockProcessor; + private readonly IBlockchainProcessor _traceProcessor; + private readonly IBlockchainProcessor _executeProcessor; private readonly ProcessingOptions _processingOptions; - public Tracer(IWorldState stateProvider, IBlockchainProcessor blockProcessor, ProcessingOptions processingOptions = ProcessingOptions.Trace) + public Tracer(IWorldState stateProvider, IBlockchainProcessor traceProcessor, IBlockchainProcessor executeProcessor, + ProcessingOptions processingOptions = ProcessingOptions.Trace) { - _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); - _blockProcessor = blockProcessor ?? throw new ArgumentNullException(nameof(blockProcessor)); + _traceProcessor = traceProcessor; + _executeProcessor = executeProcessor; + _stateProvider = stateProvider; _processingOptions = processingOptions; } - public Block? Trace(Block block, IBlockTracer blockTracer) + private void Process(Block block, IBlockTracer blockTracer, IBlockchainProcessor processor) { /* We force process since we want to process a block that has already been processed in the past and normally it would be ignored. We also want to make it read only so the state is not modified persistently in any way. */ blockTracer.StartNewBlockTrace(block); - Block? processedBlock; try { - processedBlock = _blockProcessor.Process(block, _processingOptions, blockTracer); + processor.Process(block, _processingOptions, blockTracer); } catch (Exception) { @@ -43,10 +45,12 @@ We also want to make it read only so the state is not modified persistently in a } blockTracer.EndBlockTrace(); - - return processedBlock; } + public void Trace(Block block, IBlockTracer tracer) => Process(block, tracer, _traceProcessor); + + public void Execute(Block block, IBlockTracer tracer) => Process(block, tracer, _executeProcessor); + public void Accept(ITreeVisitor visitor, Keccak stateRoot) { if (visitor is null) throw new ArgumentNullException(nameof(visitor)); diff --git a/src/Nethermind/Nethermind.Evm.Test/Nethermind.Evm.Test.csproj b/src/Nethermind/Nethermind.Evm.Test/Nethermind.Evm.Test.csproj index ad6a02a3d0c..af76747a713 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Nethermind.Evm.Test.csproj +++ b/src/Nethermind/Nethermind.Evm.Test/Nethermind.Evm.Test.csproj @@ -3,6 +3,7 @@ net7.0 false latest + annotations true diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorTraceTest.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorTraceTest.cs new file mode 100644 index 00000000000..2bf7a02c469 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorTraceTest.cs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.Int256; +using Nethermind.Specs; +using NUnit.Framework; + +namespace Nethermind.Evm.Test; + +public class TransactionProcessorTraceTest : VirtualMachineTestsBase +{ + protected override long BlockNumber => MainnetSpecProvider.GrayGlacierBlockNumber; + protected override ulong Timestamp => MainnetSpecProvider.ShanghaiBlockTimestamp; + + [TestCase(21000)] + [TestCase(50000)] + public void Trace_should_not_charge_gas(long gasLimit) + { + (Block block, Transaction transaction) = PrepareTx(BlockNumber, gasLimit); + ParityLikeTxTracer tracer = new(block, transaction, ParityTraceTypes.All); + _processor.Trace(transaction, block.Header, tracer); + var senderBalance = tracer.BuildResult().StateChanges[TestItem.AddressA].Balance; + (senderBalance.Before - senderBalance.After).Should().Be(transaction.Value); + } +} diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 88ba83bce9d..6c756cf95d0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -118,12 +118,12 @@ protected TestAllTracerWithOutput Execute(long blockNumber, long gasLimit, byte[ protected (Block block, Transaction transaction) PrepareTx( long blockNumber, long gasLimit, - byte[] code, - SenderRecipientAndMiner senderRecipientAndMiner = null, + byte[]? code = null, + SenderRecipientAndMiner? senderRecipientAndMiner = null, int value = 1, long blockGasLimit = DefaultBlockGasLimit, ulong timestamp = 0, - byte[][] blobVersionedHashes = null) + byte[][]? blobVersionedHashes = null) { senderRecipientAndMiner ??= SenderRecipientAndMiner.Default; @@ -140,7 +140,11 @@ protected TestAllTracerWithOutput Execute(long blockNumber, long gasLimit, byte[ TestState.CreateAccount(senderRecipientAndMiner.Recipient, 100.Ether()); else TestState.AddToBalance(senderRecipientAndMiner.Recipient, 100.Ether(), SpecProvider.GenesisSpec); - TestState.InsertCode(senderRecipientAndMiner.Recipient, code, SpecProvider.GenesisSpec); + + if (code is not null) + { + TestState.InsertCode(senderRecipientAndMiner.Recipient, code, SpecProvider.GenesisSpec); + } GetLogManager().GetClassLogger().Debug("Committing initial state"); TestState.Commit(SpecProvider.GenesisSpec); diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/ITransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/ITransactionProcessor.cs index 715f1d7e765..7d8ec18031a 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/ITransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/ITransactionProcessor.cs @@ -25,6 +25,7 @@ public interface ITransactionProcessor /// /// Call transaction, no validations, commit state + /// Will NOT charge gas from sender account, so stateDiff will miss gas fee /// void Trace(Transaction transaction, BlockHeader block, ITxTracer txTracer); } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 056750345ce..590e5adb8d5 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -241,7 +241,7 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra } } - UInt256 senderReservedGasPayment = noValidation ? UInt256.Zero : (ulong)gasLimit * effectiveGasPrice; + UInt256 senderReservedGasPayment = (ulong)gasLimit * effectiveGasPrice; if (notSystemTransaction) { @@ -276,7 +276,8 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra _worldState.IncrementNonce(caller); } - _worldState.SubtractFromBalance(caller, senderReservedGasPayment, spec); + // Do not charge gas if noValidation is set + if (!noValidation) _worldState.SubtractFromBalance(caller, senderReservedGasPayment, spec); if (commit) { _worldState.Commit(spec, txTracer.IsTracingState ? txTracer : NullTxTracer.Instance); @@ -393,7 +394,7 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra statusCode = StatusCode.Success; } - spentGas = Refund(gasLimit, unspentGas, substate, caller, effectiveGasPrice, spec); + spentGas = Refund(gasLimit, unspentGas, substate, caller, effectiveGasPrice, noValidation, spec); } catch (Exception ex) when ( ex is EvmException || ex is OverflowException) // TODO: OverflowException? still needed? hope not @@ -453,7 +454,7 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra } else { - _worldState.AddToBalance(caller, senderReservedGasPayment, spec); + if (!noValidation) _worldState.AddToBalance(caller, senderReservedGasPayment, spec); if (notSystemTransaction) { _worldState.DecrementNonce(caller); @@ -526,7 +527,7 @@ private void TraceLogInvalidTx(Transaction transaction, string reason) } private long Refund(long gasLimit, long unspentGas, TransactionSubstate substate, Address sender, - in UInt256 gasPrice, IReleaseSpec spec) + in UInt256 gasPrice, bool noValidation, IReleaseSpec spec) { long spentGas = gasLimit; if (!substate.IsError) @@ -539,7 +540,8 @@ private long Refund(long gasLimit, long unspentGas, TransactionSubstate substate if (_logger.IsTrace) _logger.Trace("Refunding unused gas of " + unspentGas + " and refund of " + refund); - _worldState.AddToBalance(sender, (ulong)(unspentGas + refund) * gasPrice, spec); + // If noValidation we didn't charge for gas, so do not refund + if (!noValidation) _worldState.AddToBalance(sender, (ulong)(unspentGas + refund) * gasPrice, spec); spentGas -= refund; } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs index 0303160e4f6..b956aa649f5 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs @@ -85,7 +85,7 @@ public void Setup() _blockTree.SuggestBlock(genesis); _processor.Process(genesis, ProcessingOptions.None, NullBlockTracer.Instance); - _tracer = new Tracer(stateProvider, _processor); + _tracer = new Tracer(stateProvider, _processor, _processor); } [Test] diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs index 09136c982a4..ecde467d8a8 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs @@ -57,7 +57,9 @@ public async Task Build(ISpecProvider? specProvider = null, bool isAura = false) IRewardCalculator rewardCalculator = rewardCalculatorSource.Get(txProcessingEnv.TransactionProcessor); RpcBlockTransactionsExecutor rpcBlockTransactionsExecutor = new(txProcessingEnv.TransactionProcessor, txProcessingEnv.StateProvider); - ReadOnlyChainProcessingEnv chainProcessingEnv = new( + BlockProcessor.BlockValidationTransactionsExecutor executeBlockTransactionsExecutor = new(txProcessingEnv.TransactionProcessor, + txProcessingEnv.StateProvider); + ReadOnlyChainProcessingEnv CreateChainProcessingEnv(IBlockProcessor.IBlockTransactionsExecutor transactionsExecutor) => new( txProcessingEnv, Always.Valid, Blockchain.BlockPreprocessorStep, @@ -66,9 +68,12 @@ public async Task Build(ISpecProvider? specProvider = null, bool isAura = false) dbProvider, Blockchain.SpecProvider, Blockchain.LogManager, - rpcBlockTransactionsExecutor); + transactionsExecutor); - Tracer tracer = new(chainProcessingEnv.StateProvider, chainProcessingEnv.ChainProcessor); + ReadOnlyChainProcessingEnv traceProcessingEnv = CreateChainProcessingEnv(rpcBlockTransactionsExecutor); + ReadOnlyChainProcessingEnv executeProcessingEnv = CreateChainProcessingEnv(executeBlockTransactionsExecutor); + + Tracer tracer = new(txProcessingEnv.StateProvider, traceProcessingEnv.ChainProcessor, executeProcessingEnv.ChainProcessor); TraceRpcModule = new TraceRpcModule(receiptFinder, tracer, Blockchain.BlockFinder, JsonRpcConfig, MainnetSpecProvider.Instance, LimboLogs.Instance); for (int i = 1; i < 10; i++) @@ -804,5 +809,44 @@ public async Task Trace_replayBlockTransactions_transactions_deploying_contract( string serialized = new EthereumJsonSerializer().Serialize(traces.Data); Assert.That(serialized, Is.EqualTo("[{\"output\":\"0x\",\"transactionHash\":\"0x8513c9083ec27fa8e3ca7e3ffa732d61562e2d17e2e1af6e773bc810dc4c3452\",\"action\":{\"traceAddress\":[],\"callType\":\"create\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"create\",\"creationMethod\":\"create\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x0ffd3e46594919c04bcfd4e146203c8255670828\",\"gas\":\"0x9c70\",\"value\":\"0x1\",\"input\":\"0x60006000600060006000730ffd3e46594919c04bcfd4e146203c825567082861c350f1\",\"result\":{\"gasUsed\":\"0x79\",\"output\":\"0x\",\"address\":\"0x0ffd3e46594919c04bcfd4e146203c8255670828\",\"code\":\"0x\"},\"subtraces\":[{\"traceAddress\":[0],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0x0ffd3e46594919c04bcfd4e146203c8255670828\",\"to\":\"0x0ffd3e46594919c04bcfd4e146203c8255670828\",\"gas\":\"0x9988\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]}]}},{\"output\":\"0x\",\"transactionHash\":\"0xa6a56c7927deae778a749bcdab7bbf409c0d8a5d2420021a3ba328240ae832d8\",\"action\":{\"traceAddress\":[],\"callType\":\"create\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"create\",\"creationMethod\":\"create\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x9c70\",\"value\":\"0x1\",\"input\":\"0x60006000600060006000730ffd3e46594919c04bcfd4e146203c825567082861c350f1\",\"result\":{\"gasUsed\":\"0xa3d\",\"output\":\"0x\",\"address\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"code\":\"0x\"},\"subtraces\":[{\"traceAddress\":[0],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"to\":\"0x0ffd3e46594919c04bcfd4e146203c8255670828\",\"gas\":\"0x8feb\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]}]}}]")); } + + [Test] + public async Task Trace_replayBlockTransactions_stateDiff() + { + Context context = new(); + await context.Build(); + TestRpcBlockchain blockchain = context.Blockchain; + UInt256 currentNonceAddressA = blockchain.State.GetAccount(TestItem.AddressA).Nonce; + await blockchain.AddFunds(TestItem.AddressA, 10000.Ether()); + + Transaction tx = Build.A.Transaction.WithNonce(currentNonceAddressA++) + .WithValue(10) + .WithTo(TestItem.AddressF) + .WithGasLimit(50000) + .WithGasPrice(10).SignedAndResolved(TestItem.PrivateKeyA).TestObject; + + Account accountA = blockchain.State.GetAccount(TestItem.AddressA); + Account accountD = blockchain.State.GetAccount(TestItem.AddressD); + Account accountF = blockchain.State.GetAccount(TestItem.AddressF); + + string[] traceTypes = { "stateDiff" }; + + await blockchain.AddBlock(tx); + + ResultWrapper> traces = context.TraceRpcModule.trace_replayBlockTransactions(new BlockParameter(blockchain.BlockFinder.FindLatestBlock()!.Number), traceTypes); + traces.Data.Should().HaveCount(1); + var state = traces.Data.ElementAt(0).StateChanges; + + state.Count.Should().Be(3); + state[TestItem.AddressA].Nonce.Before.Should().Be(accountA.Nonce); + state[TestItem.AddressD].Balance.Before.Should().Be(accountD.Balance); + state[TestItem.AddressA].Balance.Before.Should().Be(accountA.Balance); + state[TestItem.AddressF].Balance.Before.Should().Be(null); + + state[TestItem.AddressA].Nonce.After.Should().Be(accountA.Nonce + 1); + state[TestItem.AddressD].Balance.After.Should().Be(accountD.Balance + 21000 * tx.GasPrice); + state[TestItem.AddressA].Balance.After.Should().Be(accountA.Balance - 21000 * tx.GasPrice - tx.Value); + state[TestItem.AddressF].Balance.After.Should().Be(accountF.Balance + tx.Value); + } } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofModuleFactory.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofModuleFactory.cs index 23f704e44dc..adf28739a1c 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofModuleFactory.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Proof/ProofModuleFactory.cs @@ -57,6 +57,7 @@ public override IProofRpcModule Create() Tracer tracer = new( txProcessingEnv.StateProvider, + chainProcessingEnv.ChainProcessor, chainProcessingEnv.ChainProcessor); return new ProofRpcModule(tracer, _blockTree, _receiptFinder, _specProvider, _logManager); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceModuleFactory.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceModuleFactory.cs index ab664333549..43d8977a8e2 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceModuleFactory.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceModuleFactory.cs @@ -10,11 +10,8 @@ using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Tracing; using Nethermind.Consensus.Validators; -using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Db; -using Nethermind.Evm.TransactionProcessing; -using Nethermind.JsonRpc.Data; using Nethermind.Logging; using Nethermind.Trie.Pruning; using Newtonsoft.Json; @@ -69,8 +66,10 @@ public override ITraceRpcModule Create() _poSSwitcher); RpcBlockTransactionsExecutor rpcBlockTransactionsExecutor = new(txProcessingEnv.TransactionProcessor, txProcessingEnv.StateProvider); + BlockProcessor.BlockValidationTransactionsExecutor executeBlockTransactionsExecutor = new(txProcessingEnv.TransactionProcessor, + txProcessingEnv.StateProvider); - ReadOnlyChainProcessingEnv chainProcessingEnv = new( + ReadOnlyChainProcessingEnv CreateChainProcessingEnv(IBlockProcessor.IBlockTransactionsExecutor transactionsExecutor) => new( txProcessingEnv, Always.Valid, _recoveryStep, @@ -79,9 +78,12 @@ public override ITraceRpcModule Create() _dbProvider, _specProvider, _logManager, - rpcBlockTransactionsExecutor); + transactionsExecutor); - Tracer tracer = new(chainProcessingEnv.StateProvider, chainProcessingEnv.ChainProcessor); + ReadOnlyChainProcessingEnv traceProcessingEnv = CreateChainProcessingEnv(rpcBlockTransactionsExecutor); + ReadOnlyChainProcessingEnv executeProcessingEnv = CreateChainProcessingEnv(executeBlockTransactionsExecutor); + + Tracer tracer = new(txProcessingEnv.StateProvider, traceProcessingEnv.ChainProcessor, executeProcessingEnv.ChainProcessor); return new TraceRpcModule(_receiptStorage, tracer, _blockTree, _jsonRpcConfig, _specProvider, _logManager); } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs index 11b552bb781..ebaf494751c 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs @@ -22,6 +22,14 @@ namespace Nethermind.JsonRpc.Modules.Trace { + /// + /// All methods that receive transaction from users uses ITransactionProcessor.Trace + /// As user might send transaction without gas and/or sender we can't charge gas fees here + /// So at the end stateDiff will be a bit incorrect + /// + /// All methods that traces transactions from chain uses ITransactionProcessor.Execute + /// From-chain transactions should have stateDiff as we got during normal execution. Also we are sure that sender have enough funds to pay gas + /// public class TraceRpcModule : ITraceRpcModule { private readonly IReceiptFinder _receiptFinder; @@ -49,6 +57,9 @@ public TraceRpcModule(IReceiptFinder? receiptFinder, ITracer? tracer, IBlockFind public static ParityTraceTypes GetParityTypes(string[] types) => types.Select(s => FastEnum.Parse(s, true)).Aggregate((t1, t2) => t1 | t2); + /// + /// Traces one transaction. Doesn't charge fees. + /// public ResultWrapper trace_call(TransactionForRpc call, string[] traceTypes, BlockParameter? blockParameter = null) { blockParameter ??= BlockParameter.Latest; @@ -59,6 +70,9 @@ public ResultWrapper trace_call(TransactionForRpc call, return TraceTx(tx, traceTypes, blockParameter); } + /// + /// Traces list of transactions. Doesn't charge fees. + /// public ResultWrapper> trace_callMany(TransactionForRpcWithTraceTypes[] calls, BlockParameter? blockParameter = null) { blockParameter ??= BlockParameter.Latest; @@ -86,6 +100,9 @@ public ResultWrapper> trace_callMany(Transa return ResultWrapper>.Success(traces.Select(t => new ParityTxTraceFromReplay(t))); } + /// + /// Traces one raw transaction. Doesn't charge fees. + /// public ResultWrapper trace_rawTransaction(byte[] data, string[] traceTypes) { Transaction tx = _txDecoder.Decode(new RlpStream(data), RlpBehaviors.SkipTypedWrapping); @@ -136,6 +153,9 @@ private ResultWrapper TraceTx(Transaction tx, string[] return ResultWrapper.Success(new ParityTxTraceFromReplay(result.SingleOrDefault())); } + /// + /// Traces one transaction. As it replays existing transaction will charge gas + /// public ResultWrapper trace_replayTransaction(Keccak txHash, string[] traceTypes) { SearchResult blockHashSearch = _receiptFinder.SearchForReceiptBlockHash(txHash); @@ -152,10 +172,13 @@ public ResultWrapper trace_replayTransaction(Keccak txH Block block = blockSearch.Object!; - IReadOnlyCollection? txTrace = TraceBlock(block, new ParityLikeBlockTracer(txHash, GetParityTypes(traceTypes))); + IReadOnlyCollection? txTrace = ExecuteBlock(block, new ParityLikeBlockTracer(txHash, GetParityTypes(traceTypes))); return ResultWrapper.Success(new ParityTxTraceFromReplay(txTrace)); } + /// + /// Traces one block. As it replays existing block will charge gas + /// public ResultWrapper> trace_replayBlockTransactions(BlockParameter blockParameter, string[] traceTypes) { SearchResult blockSearch = _blockFinder.SearchForBlock(blockParameter); @@ -167,12 +190,15 @@ public ResultWrapper> trace_replayBlockTran Block block = blockSearch.Object!; ParityTraceTypes traceTypes1 = GetParityTypes(traceTypes); - IReadOnlyCollection txTraces = TraceBlock(block, new(traceTypes1)); + IReadOnlyCollection txTraces = ExecuteBlock(block, new(traceTypes1)); // ReSharper disable once CoVariantArrayConversion return ResultWrapper>.Success(txTraces.Select(t => new ParityTxTraceFromReplay(t, true))); } + /// + /// Traces blocks specified in filter. As it replays existing transaction will charge gas + /// public ResultWrapper> trace_filter(TraceFilterForRpc traceFilterForRpc) { List txTraces = new(); @@ -188,7 +214,7 @@ public ResultWrapper> trace_filter(TraceFilt } Block block = blockSearch.Object; - IReadOnlyCollection txTracesFromOneBlock = TraceBlock(block!, new((ParityTraceTypes)(ParityTraceTypes.Trace | ParityTraceTypes.Rewards))); + IReadOnlyCollection txTracesFromOneBlock = ExecuteBlock(block!, new((ParityTraceTypes)(ParityTraceTypes.Trace | ParityTraceTypes.Rewards))); txTraces.AddRange(txTracesFromOneBlock); } @@ -207,10 +233,13 @@ public ResultWrapper> trace_block(BlockParam } Block block = blockSearch.Object!; - IReadOnlyCollection txTraces = TraceBlock(block, new((ParityTraceTypes)(ParityTraceTypes.Trace | ParityTraceTypes.Rewards))); + IReadOnlyCollection txTraces = ExecuteBlock(block, new((ParityTraceTypes)(ParityTraceTypes.Trace | ParityTraceTypes.Rewards))); return ResultWrapper>.Success(txTraces.SelectMany(ParityTxTraceFromStore.FromTxTrace)); } + /// + /// Traces one transaction. As it replays existing transaction will charge gas + /// public ResultWrapper> trace_get(Keccak txHash, long[] positions) { ResultWrapper> traceTransaction = trace_transaction(txHash); @@ -235,6 +264,9 @@ public static List ExtractPositionsFromTxTrace(long[] po return traces; } + /// + /// Traces one transaction. As it replays existing transaction will charge gas + /// public ResultWrapper> trace_transaction(Keccak txHash) { SearchResult blockHashSearch = _receiptFinder.SearchForReceiptBlockHash(txHash); @@ -251,7 +283,7 @@ public ResultWrapper> trace_transaction(Kecc Block block = blockSearch.Object!; - IReadOnlyCollection txTrace = TraceBlock(block, new(txHash, ParityTraceTypes.Trace)); + IReadOnlyCollection txTrace = ExecuteBlock(block, new(txHash, ParityTraceTypes.Trace)); return ResultWrapper>.Success(ParityTxTraceFromStore.FromTxTrace(txTrace)); } @@ -262,5 +294,13 @@ private IReadOnlyCollection TraceBlock(Block block, ParityLik _tracer.Trace(block, tracer.WithCancellation(cancellationToken)); return tracer.BuildResult(); } + + private IReadOnlyCollection ExecuteBlock(Block block, ParityLikeBlockTracer tracer) + { + using CancellationTokenSource cancellationTokenSource = new(_cancellationTokenTimeout); + CancellationToken cancellationToken = cancellationTokenSource.Token; + _tracer.Execute(block, tracer.WithCancellation(cancellationToken)); + return tracer.BuildResult(); + } } } diff --git a/src/Nethermind/Nethermind.Mev/Execution/TracerFactory.cs b/src/Nethermind/Nethermind.Mev/Execution/TracerFactory.cs index c9bda5fd8f0..d09f9f9955c 100644 --- a/src/Nethermind/Nethermind.Mev/Execution/TracerFactory.cs +++ b/src/Nethermind/Nethermind.Mev/Execution/TracerFactory.cs @@ -55,6 +55,6 @@ public ITracer Create() } protected virtual ITracer CreateTracer(ReadOnlyTxProcessingEnv txProcessingEnv, ReadOnlyChainProcessingEnv chainProcessingEnv) => - new Tracer(txProcessingEnv.StateProvider, chainProcessingEnv.ChainProcessor, _processingOptions); + new Tracer(txProcessingEnv.StateProvider, chainProcessingEnv.ChainProcessor, chainProcessingEnv.ChainProcessor, _processingOptions); } }