diff --git a/.github/workflows/run-nethermind-tests-with-code-coverage.yml b/.github/workflows/run-nethermind-tests-with-code-coverage.yml index 7cabadcc3ce..caa37594451 100644 --- a/.github/workflows/run-nethermind-tests-with-code-coverage.yml +++ b/.github/workflows/run-nethermind-tests-with-code-coverage.yml @@ -175,6 +175,9 @@ jobs: - name: Nethermind.Sockets.Test run: | dotnet test -c Release $EXCLUDE_TEST_PROJECTS src/Nethermind/Nethermind.Sockets.Test + - name: Nethermind.JsonRpc.TraceStore.Tests + run: | + dotnet test -c Release $EXCLUDE_TEST_PROJECTS src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests - name: Upload Codecov Report if: matrix.os == 'ubuntu-latest' uses: actions/upload-artifact@v2 diff --git a/.github/workflows/run-nethermind-tests.yml b/.github/workflows/run-nethermind-tests.yml index fee65bf10eb..536758697da 100644 --- a/.github/workflows/run-nethermind-tests.yml +++ b/.github/workflows/run-nethermind-tests.yml @@ -45,6 +45,9 @@ jobs: - name: Nethermind.AccountAbstraction.Test run: | dotnet test src/Nethermind/Nethermind.AccountAbstraction.Test -c ${{ env.BUILD_CONFIG }} + - name: Nethermind.JsonRpc.TraceStore.Tests + run: | + dotnet test src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests -c ${{ env.BUILD_CONFIG }} neth-tests2: name: Running Nethermind Tests 2 diff --git a/scripts/deployment/archive-packages.sh b/scripts/deployment/archive-packages.sh index 86ac4ee6789..08a5479df4d 100755 --- a/scripts/deployment/archive-packages.sh +++ b/scripts/deployment/archive-packages.sh @@ -35,11 +35,11 @@ dotnet build -c release Nethermind.Runner.csproj cd $RELEASE_DIRECTORY -cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev}.dll $LIN_RELEASE/plugins -cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev}.dll $OSX_RELEASE/plugins -cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev}.dll $WIN_RELEASE/plugins -cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev}.dll $LIN_ARM64_RELEASE/plugins -cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev}.dll $OSX_ARM64_RELEASE/plugins +cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev,JsonRpc.TraceStore}.dll $LIN_RELEASE/plugins +cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev,JsonRpc.TraceStore}.dll $OSX_RELEASE/plugins +cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev,JsonRpc.TraceStore}.dll $WIN_RELEASE/plugins +cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev,JsonRpc.TraceStore}.dll $LIN_ARM64_RELEASE/plugins +cp $RELEASE_DIRECTORY/$RELEASE_PATH/Nethermind.{Api,HealthChecks,EthStats,Merge.Plugin,Mev,JsonRpc.TraceStore}.dll $OSX_ARM64_RELEASE/plugins cd $LIN_RELEASE && zip -r $LIN-$VERSION-$COMMIT_HASH-$DATE.zip . && cd .. cd $OSX_RELEASE && zip -r $OSX-$VERSION-$COMMIT_HASH-$DATE.zip . && cd .. diff --git a/src/Nethermind/Directory.Build.props b/src/Nethermind/Directory.Build.props index 9eae93a3e38..2f48d62cfa1 100644 --- a/src/Nethermind/Directory.Build.props +++ b/src/Nethermind/Directory.Build.props @@ -11,7 +11,7 @@ Demerzel Solutions Limited Nethermind $(Commit.Substring(0, 8)) - 1.14.6 + 1.14.7 diff --git a/src/Nethermind/Nethermind.Consensus/BlocksConfig.cs b/src/Nethermind/Nethermind.Consensus/BlocksConfig.cs index 0a35c9330b3..74ccd6aefe1 100644 --- a/src/Nethermind/Nethermind.Consensus/BlocksConfig.cs +++ b/src/Nethermind/Nethermind.Consensus/BlocksConfig.cs @@ -1,19 +1,5 @@ -// 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 . -// +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only using System; using System.Reflection; diff --git a/src/Nethermind/Nethermind.Consensus/IBlocksConfig.cs b/src/Nethermind/Nethermind.Consensus/IBlocksConfig.cs index 126c39cf714..883979611c7 100644 --- a/src/Nethermind/Nethermind.Consensus/IBlocksConfig.cs +++ b/src/Nethermind/Nethermind.Consensus/IBlocksConfig.cs @@ -1,18 +1,5 @@ -// 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 . +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; using System.Reflection; diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 7e15499e59a..b7ecf989966 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -223,10 +223,10 @@ protected virtual TxReceipt[] ProcessBlock( _receiptsTracer.SetOtherTracer(blockTracer); _receiptsTracer.StartNewBlockTrace(block); TxReceipt[] receipts = _blockTransactionsExecutor.ProcessTransactions(block, options, _receiptsTracer, spec); - _receiptsTracer.EndBlockTrace(); block.Header.ReceiptsRoot = receipts.GetReceiptsRoot(spec, block.ReceiptsRoot); ApplyMinerRewards(block, blockTracer, spec); + _receiptsTracer.EndBlockTrace(); _stateProvider.Commit(spec); _stateProvider.RecalculateStateRoot(); diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockRef.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockRef.cs index 45a1a5915d7..d36ada35785 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockRef.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockRef.cs @@ -46,5 +46,7 @@ public bool Resolve(IBlockTree blockTree) return true; } + + public override string ToString() => Block?.ToString() ?? BlockHash.ToString(); } } diff --git a/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListTests.cs b/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListTests.cs index 21489c39657..239fd3ba7b2 100644 --- a/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Collections/ArrayPoolListTests.cs @@ -175,5 +175,18 @@ public void Set_should_throw(int item) Action action = () => list[item] = 1; action.Should().Throw(); } + + [TestCase(1, 16)] + [TestCase(14, 16)] + [TestCase(15, 32)] + [TestCase(20, 32)] + [TestCase(100, 128)] + public void AddRange_should_expand(int items, int expectedCapacity) + { + ArrayPoolList list = new(16) { 0, 1 }; + list.AddRange(Enumerable.Range(2, items)); + list.Should().BeEquivalentTo(Enumerable.Range(0, items + 2)); + list.Capacity.Should().Be(expectedCapacity); + } } } diff --git a/src/Nethermind/Nethermind.Core/BlockInfo.cs b/src/Nethermind/Nethermind.Core/BlockInfo.cs index 27f4aeebd60..52fc8f7e8ff 100644 --- a/src/Nethermind/Nethermind.Core/BlockInfo.cs +++ b/src/Nethermind/Nethermind.Core/BlockInfo.cs @@ -76,5 +76,7 @@ public bool IsBeaconInfo /// This property is not serialized /// public long BlockNumber { get; set; } + + public override string ToString() => BlockHash.ToString(); } } diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs index 4e870523ae2..e5292e4386e 100644 --- a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs @@ -7,15 +7,13 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; -using System.Threading.Tasks; -using Nethermind.Core.Crypto; namespace Nethermind.Core.Collections { public class ArrayPoolList : IList, IReadOnlyList, IDisposable { private readonly ArrayPool _arrayPool; - private T[] _array; + protected T[] _array; private int _count = 0; private int _capacity; private bool _disposed; @@ -60,6 +58,13 @@ public void Add(T item) _array[_count++] = item; } + public void AddRange(Span items) + { + GuardResize(items.Length); + items.CopyTo(_array.AsSpan(_count, items.Length)); + _count += items.Length; + } + public void Clear() { _count = 0; @@ -100,12 +105,17 @@ public void Insert(int index, T item) _count++; } - private void GuardResize() + private void GuardResize(int itemsToAdd = 1) { GuardDispose(); - if (_count == _capacity) + int newCount = _count + itemsToAdd; + if (newCount > _capacity) { int newCapacity = _capacity * 2; + while (newCount > newCapacity) + { + newCapacity *= 2; + } T[] newArray = _arrayPool.Rent(newCapacity); _array.CopyTo(newArray, 0); T[] oldArray = Interlocked.Exchange(ref _array, newArray); @@ -198,5 +208,7 @@ public void Dispose() _disposed = true; } } + + public Span AsSpan() => _array.AsSpan(0, _count); } } diff --git a/src/Nethermind/Nethermind.Core/Collections/ByteArrayPoolList.cs b/src/Nethermind/Nethermind.Core/Collections/ByteArrayPoolList.cs new file mode 100644 index 00000000000..e29a61473e5 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Collections/ByteArrayPoolList.cs @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Buffers; +using System.Collections.Generic; +using System.IO; + +namespace Nethermind.Core.Collections; + +public class ByteArrayPoolList : ArrayPoolList +{ + public ByteArrayPoolList(int capacity) : base(capacity) + { + } + + public ByteArrayPoolList(int capacity, IEnumerable enumerable) : base(capacity, enumerable) + { + } + + public ByteArrayPoolList(ArrayPool arrayPool, int capacity) : base(arrayPool, capacity) + { + } + + public MemoryStream AsMemoryStream() => new(_array, 0, Count); +} diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index 08423bc5d8a..1ea0d825f7e 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -504,20 +504,8 @@ public static void StreamHex(this byte[] bytes, StreamWriter streamWriter) } } - public static string ToHexString(this byte[] bytes, bool withZeroX) - { - return ToHexString(bytes, withZeroX, false, false); - } - - public static string ToHexString(this byte[] bytes, bool withZeroX, bool noLeadingZeros) - { - return ToHexString(bytes, withZeroX, noLeadingZeros, false); - } - - public static string ToHexString(this byte[] bytes, bool withZeroX, bool noLeadingZeros, bool withEip55Checksum) - { - return ByteArrayToHexViaLookup32(bytes, withZeroX, noLeadingZeros, withEip55Checksum); - } + public static string ToHexString(this byte[] bytes, bool withZeroX, bool noLeadingZeros = false, bool withEip55Checksum = false) => + ByteArrayToHexViaLookup32(bytes, withZeroX, noLeadingZeros, withEip55Checksum); private struct StateSmall { diff --git a/src/Nethermind/Nethermind.Core/Resettables/ResettableList.cs b/src/Nethermind/Nethermind.Core/Resettables/ResettableList.cs index 404a827b2ca..fbadec7b090 100644 --- a/src/Nethermind/Nethermind.Core/Resettables/ResettableList.cs +++ b/src/Nethermind/Nethermind.Core/Resettables/ResettableList.cs @@ -7,9 +7,9 @@ namespace Nethermind.Core.Resettables; -public class ResettableList : IList +public class ResettableList : IList, IReadOnlyCollection { - private List _wrapped; + private readonly List _wrapped; private readonly int _startCapacity; private readonly int _resetRatio; private int _currentCapacity; diff --git a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs index 06537770272..8cfb5cba3f5 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs @@ -196,9 +196,11 @@ protected internal void UpdateWriteMetrics() propertyName); try { - return (T?)dbConfig.GetType() - .GetProperty(prefixed, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)? - .GetValue(dbConfig); + Type type = dbConfig.GetType(); + PropertyInfo? propertyInfo = type.GetProperty(prefixed, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + // if no custom db property default to generic one + propertyInfo ??= type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + return (T?)propertyInfo?.GetValue(dbConfig); } catch (Exception e) { diff --git a/src/Nethermind/Nethermind.Db/RocksDbInitializer.cs b/src/Nethermind/Nethermind.Db/RocksDbInitializer.cs index 359446ded33..e9a659c7a4d 100644 --- a/src/Nethermind/Nethermind.Db/RocksDbInitializer.cs +++ b/src/Nethermind/Nethermind.Db/RocksDbInitializer.cs @@ -68,9 +68,6 @@ protected async Task InitAllAsync() await Task.WhenAll(allInitializers); } - protected static string GetTitleDbName(string dbName) - { - return char.ToUpper(dbName[0]) + dbName.Substring(1); - } + protected static string GetTitleDbName(string dbName) => char.ToUpper(dbName[0]) + dbName.Substring(1); } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/BlockTracerBase.cs b/src/Nethermind/Nethermind.Evm/Tracing/BlockTracerBase.cs index e240f6c6033..34a74ca56d4 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/BlockTracerBase.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/BlockTracerBase.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Resettables; using Nethermind.Int256; namespace Nethermind.Evm.Tracing @@ -16,13 +17,13 @@ public abstract class BlockTracerBase : IBlockTracer where TTra protected BlockTracerBase() { - TxTraces = new List(); + TxTraces = new ResettableList(); } protected BlockTracerBase(Keccak? txHash) { _txHash = txHash; - TxTraces = new List(); + TxTraces = new ResettableList(); } private TTracer? CurrentTxTracer { get; set; } @@ -36,7 +37,10 @@ public virtual void ReportReward(Address author, string rewardType, UInt256 rewa { } - public abstract void StartNewBlockTrace(Block block); + public virtual void StartNewBlockTrace(Block block) + { + TxTraces.Reset(); + } ITxTracer IBlockTracer.StartNewTxTrace(Transaction? tx) { @@ -58,18 +62,15 @@ void IBlockTracer.EndTxTrace() } } - public abstract void EndBlockTrace(); + public virtual void EndBlockTrace() { } protected virtual bool ShouldTraceTx(Transaction? tx) { return IsTracingEntireBlock || tx?.Hash == _txHash; } - protected List TxTraces { get; } + protected ResettableList TxTraces { get; } - public IReadOnlyCollection BuildResult() - { - return TxTraces; - } + public IReadOnlyCollection BuildResult() => TxTraces; } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockTracer.cs index b3534128577..4dd03ca6f5e 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockTracer.cs @@ -24,13 +24,5 @@ public GethLikeBlockTracer(Keccak txHash, GethTraceOptions options) protected override GethLikeTxTracer OnStart(Transaction? tx) => new(_options); protected override GethLikeTxTrace OnEnd(GethLikeTxTracer txTracer) => txTracer.BuildResult(); - - public override void StartNewBlockTrace(Block block) - { - } - - public override void EndBlockTrace() - { - } } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/IBlockTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/IBlockTracer.cs index 360332c64e7..9e9aad8b2e9 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/IBlockTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/IBlockTracer.cs @@ -15,16 +15,45 @@ namespace Nethermind.Evm.Tracing /// public interface IBlockTracer { + /// + /// Is reward state change traced + /// + /// + /// Controls + /// - + /// bool IsTracingRewards { get; } + /// + /// Reports rewards for bock. + /// + /// Author/coinbase for reward. + /// Type of reward. + /// Value of reward. + /// Depends on void ReportReward(Address author, string rewardType, UInt256 rewardValue); + /// + /// Starts a trace for new block. + /// + /// Block to be traced. void StartNewBlockTrace(Block block); + /// + /// Starts new transaction trace in a block. + /// + /// Transaction this trace is started for. Null if it's reward trace. + /// Returns tracer for transaction. ITxTracer StartNewTxTrace(Transaction? tx); + /// + /// Ends last transaction trace . + /// void EndTxTrace(); + /// + /// Ends block trace . + /// void EndBlockTrace(); } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs index e6bc2a03ee3..f6400a3a87b 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs @@ -16,83 +16,234 @@ public interface ITxTracer : IStateTracer, IStorageTracer /// /// Defines whether MarkAsSuccess or MarkAsFailed will be called /// + /// + /// Controls + /// - + /// - + /// bool IsTracingReceipt { get; } + /// /// High level calls with information on the target account /// + /// + /// Controls + /// - + /// - + /// - + /// - + /// bool IsTracingActions { get; } + /// /// SSTORE and SLOAD level storage operations /// + /// + /// Controls + /// - + /// - + /// - + /// - + /// bool IsTracingOpLevelStorage { get; } + /// /// EVM memory access operations /// + /// + /// Controls + /// - + /// - + /// bool IsTracingMemory { get; } + + /// + /// EVM instructions + /// + /// + /// Controls + /// - + /// - + /// - + /// - + /// - + /// - + /// bool IsTracingInstructions { get; } + /// /// Updates of refund counter /// + /// + /// Controls + /// - + /// - + /// bool IsTracingRefunds { get; } + /// /// Code deployment /// + /// + /// Controls + /// - + /// bool IsTracingCode { get; } + /// /// EVM stack tracing after each operation /// + /// + /// Controls + /// - + /// bool IsTracingStack { get; } /// /// Traces blockhash calls /// + /// + /// Controls + /// - + /// bool IsTracingBlockHash { get; } /// /// Traces storage access /// + /// + /// Controls + /// - + /// bool IsTracingAccess { get; } /// /// Traces fees and burned fees /// + /// + /// Controls + /// - + /// bool IsTracingFees { get; } + /// + /// Transaction completed successfully + /// + /// Transaction recipient + /// Gas spent on transaction execution + /// Output of transaction + /// Logs for transaction + /// State root after transaction, depends on EIP-658 + /// Depends on void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Keccak? stateRoot = null); + /// + /// Transaction failed + /// + /// Transaction recipient + /// Gas spent on transaction execution + /// Output of transaction + /// Error that failed the transaction + /// State root after transaction, depends on EIP-658 + /// Depends on void MarkAsFailed(Address recipient, long gasSpent, byte[] output, string error, Keccak? stateRoot = null); + /// + /// + /// + /// + /// + /// + /// + /// + /// Depends on void StartOperation(int depth, long gas, Instruction opcode, int pc, bool isPostMerge = false); + /// + /// + /// + /// + /// Depends on void ReportOperationError(EvmExceptionType error); + /// + /// + /// + /// + /// Depends on void ReportOperationRemainingGas(long gas); + /// + /// + /// + /// + /// Depends on void SetOperationStack(List stackTrace); + /// + /// + /// + /// + /// Depends on void ReportStackPush(in ReadOnlySpan stackItem); + /// + + /// + /// + /// Depends on void ReportStackPush(byte stackItem) { ReportStackPush(new[] { stackItem }); } + /// + /// + /// + /// + /// Depends on void ReportStackPush(in ZeroPaddedSpan stackItem) { ReportStackPush(stackItem.ToArray().AsSpan()); } + /// + /// + /// + /// Depends on void ReportStackPush(in ZeroPaddedMemory stackItem) { ReportStackPush(stackItem.ToArray().AsSpan()); } + /// + /// + /// + /// + /// Depends on void SetOperationMemory(List memoryTrace); + /// + /// + /// + /// + /// Depends on void SetOperationMemorySize(ulong newSize); + /// + /// + /// + /// + /// + /// Depends on void ReportMemoryChange(long offset, in ReadOnlySpan data); + /// + /// + /// + /// + /// + /// Depends on void ReportMemoryChange(UInt256 offset, in ReadOnlySpan data) { if (offset.u1 <= 0 && offset.u2 <= 0 && offset.u3 <= 0 && offset.u0 <= long.MaxValue) @@ -101,43 +252,142 @@ void ReportMemoryChange(UInt256 offset, in ReadOnlySpan data) } } + /// + /// + /// + /// + /// + /// Depends on void ReportMemoryChange(long offset, byte data) { ReportMemoryChange(offset, new[] { data }); } + /// + /// + /// + /// + /// + /// Depends on void ReportMemoryChange(long offset, in ZeroPaddedSpan data) { ReportMemoryChange(offset, data.ToArray()); } + /// + /// + /// + /// + /// Depends on void ReportMemoryChange(long offset, in ZeroPaddedMemory data) { ReportMemoryChange(offset, data.ToArray()); } - void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value); - + /// + /// + /// + /// + /// + /// + /// + /// Depends on void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue); + + /// + /// + /// + /// + /// + /// + /// + /// Depends on void SetOperationTransientStorage(Address storageCellAddress, UInt256 storageIndex, Span newValue, byte[] currentValue) { } + /// + /// + /// + /// + /// + /// + /// Depends on void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value); + + /// + /// + /// + /// + /// + /// + /// Depends on void LoadOperationTransientStorage(Address storageCellAddress, UInt256 storageIndex, byte[] value) { } + /// + /// + /// + /// + /// + /// + /// Depends on void ReportSelfDestruct(Address address, UInt256 balance, Address refundAddress); + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Depends on void ReportAction(long gas, UInt256 value, Address @from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false); + /// + /// + /// + /// + /// + /// Depends on void ReportActionEnd(long gas, ReadOnlyMemory output); + /// + /// + /// + /// + /// + /// Depends on void ReportActionError(EvmExceptionType evmExceptionType, long gasLeft) => ReportActionError(evmExceptionType); + /// + /// + /// + /// + /// Depends on void ReportActionError(EvmExceptionType evmExceptionType); + /// + /// + /// + /// + /// + /// + /// Depends on void ReportActionEnd(long gas, Address deploymentAddress, ReadOnlyMemory deployedCode); + /// + /// + /// + /// + /// Depends on void ReportBlockHash(Keccak blockHash); + /// + /// + /// + /// + /// Depends on void ReportByteCode(byte[] byteCode); /// @@ -145,11 +395,37 @@ void LoadOperationTransientStorage(Address storageCellAddress, UInt256 storageIn /// /// /// + /// Depends on void ReportGasUpdateForVmTrace(long refund, long gasAvailable); + /// + /// + /// + /// + /// Depends on void ReportRefund(long refund); + + /// + /// + /// + /// + /// Depends on void ReportExtraGasPressure(long extraGasPressure); + + /// + /// Reports access to storage cell + /// + /// address + /// cell + /// Depends on void ReportAccess(IReadOnlySet
accessedAddresses, IReadOnlySet accessedStorageCells); + + /// + /// Reports fees of a transaction + /// + /// Fees sent to block author + /// EIP-1559 burnt fees + /// Depends on void ReportFees(UInt256 fees, UInt256 burntFees); } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeBlockTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeBlockTracer.cs index f17c8e81cfe..62e6c1e8964 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeBlockTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeBlockTracer.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using System.Linq; using Nethermind.Core; @@ -52,7 +53,7 @@ public override void ReportReward(Address author, string rewardType, UInt256 rew Value = rewardValue, Author = author, CallType = "reward", - TraceAddress = new int[] { }, + TraceAddress = Array.Empty(), Type = "reward", Result = null }; @@ -62,10 +63,7 @@ public override void ReportReward(Address author, string rewardType, UInt256 rew public override void StartNewBlockTrace(Block block) { _block = block; - } - - public override void EndBlockTrace() - { + base.StartNewBlockTrace(block); } } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs index e0960caa7ed..0fdbcd8e8e9 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs @@ -37,7 +37,7 @@ public ParityLikeTxTracer(Block block, Transaction? tx, ParityTraceTypes parityT _trace = new ParityLikeTxTrace { TransactionHash = tx?.Hash, - TransactionPosition = tx is null ? (int?)null : Array.IndexOf(block.Transactions!, tx), + TransactionPosition = tx is null ? null : Array.IndexOf(block.Transactions!, tx), BlockNumber = block.Number, BlockHash = block.Hash! }; diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityResult.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityResult.cs index 9a09806e0a4..a84a116c226 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityResult.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityResult.cs @@ -8,8 +8,8 @@ namespace Nethermind.Evm.Tracing.ParityStyle public class ParityTraceResult { public long GasUsed { get; set; } - public byte[] Output { get; set; } - public Address Address { get; set; } - public byte[] Code { get; set; } + public byte[]? Output { get; set; } + public Address? Address { get; set; } + public byte[]? Code { get; set; } } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Proofs/ProofBlockTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Proofs/ProofBlockTracer.cs index 94d1537e368..f0c632b61e1 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Proofs/ProofBlockTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Proofs/ProofBlockTracer.cs @@ -23,13 +23,5 @@ public ProofBlockTracer(Keccak? txHash, bool treatSystemAccountDifferently) : ba /// Tracer of the transaction that just has been processed. /// Just returns the protected override ProofTxTracer OnEnd(ProofTxTracer txTracer) => txTracer; - - public override void StartNewBlockTrace(Block block) - { - } - - public override void EndBlockTrace() - { - } } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcModuleProviderTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcModuleProviderTests.cs index f389ebcbfa6..38555e54510 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcModuleProviderTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcModuleProviderTests.cs @@ -94,5 +94,25 @@ public void Method_resolution_is_scoped_to_url_enabled_modules() ModuleResolution fallbackResolution = _moduleProvider.Check("proof_call", new JsonRpcContext(RpcEndpoint.Http)); Assert.AreEqual(ModuleResolution.Enabled, fallbackResolution); } + + [Test] + public void Allows_to_get_modules() + { + SingletonModulePool pool = new(Substitute.For()); + _moduleProvider.Register(pool); + _moduleProvider.GetPool(ModuleType.Net).Should().Be(pool); + } + + [Test] + public void Allows_to_replace_modules() + { + SingletonModulePool pool = new(Substitute.For()); + _moduleProvider.Register(pool); + + SingletonModulePool pool2 = new(Substitute.For()); + _moduleProvider.Register(pool2); + + _moduleProvider.GetPool(ModuleType.Net).Should().Be(pool2); + } } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcModuleProvider.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcModuleProvider.cs index ce88d758e95..abf6bf8de2e 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcModuleProvider.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcModuleProvider.cs @@ -59,24 +59,14 @@ private void EnableModule() where TOther : IRpcModule public IReadOnlyCollection Converters => _provider.Converters; public IReadOnlyCollection Enabled => _provider.All; public IReadOnlyCollection All => _provider.All; - public ModuleResolution Check(string methodName, JsonRpcContext context) - { - return _provider.Check(methodName, context); - } + public ModuleResolution Check(string methodName, JsonRpcContext context) => _provider.Check(methodName, context); - public (MethodInfo, bool) Resolve(string methodName) - { - return _provider.Resolve(methodName); - } + public (MethodInfo, bool) Resolve(string methodName) => _provider.Resolve(methodName); - public Task Rent(string methodName, bool readOnly) - { - return _provider.Rent(methodName, readOnly); - } + public Task Rent(string methodName, bool readOnly) => _provider.Rent(methodName, readOnly); - public void Return(string methodName, IRpcModule rpcModule) - { - _provider.Return(methodName, rpcModule); - } + public void Return(string methodName, IRpcModule rpcModule) => _provider.Return(methodName, rpcModule); + + public IRpcModulePool? GetPool(string moduleType) => _provider.GetPool(moduleType); } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs index ef3782d3b78..63c37833776 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TraceRpcModuleTests.cs @@ -97,7 +97,7 @@ public async Task Tx_positions_are_fine() string serialized = RpcTest.TestSerializedRequest( EthModuleFactory.Converters.Union(TraceModuleFactory.Converters).ToList(), context.TraceRpcModule, "trace_block", "latest"); - Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xa1e0e640b433d5a8931881b8eee7b1a125474b04e430c0bf8afff52584c53273\",\"transactionPosition\":0,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x5cf5d4a0a93000beb1cfb373508ce4c0153ab491be99b3c927f482346c86a0e1\",\"transactionPosition\":1,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x02d2cde9120e37722f607771ebaa0d4e98c5d99a8a9e7df6872e8c8c9f5c0bc5\",\"transactionPosition\":2,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xe50a2a2d170011b1f9ee080c3810bed0c63dbb1b2b2c541c78ada5b222cc3fd2\",\"transactionPosition\":3,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xff0d4524d379fc15c41a9b0444b943e1a530779b7d09c8863858267c5ef92b24\",\"transactionPosition\":4,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xf9b69366c82084e3799dc4a7ad87dc173ef4923d853bc250de86b81786f2972a\",\"transactionPosition\":5,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x28171c29b23cd96f032fe43f444402af4555ee5f074d5d0d0a1089d940f136e7\",\"transactionPosition\":6,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x09b01caf4b7ecfe9d02251b2e478f2da0fdf08412e3fa1ff963fa80635dab031\",\"transactionPosition\":7,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xd82382905afbe4ca4c2b8e54cea43818c91e0014c3827e3020fbd82b732b8239\",\"transactionPosition\":8,\"type\":\"call\"},{\"action\":{\"author\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"rewardType\":\"block\",\"value\":\"0x1bc16d674ec80000\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":null,\"subtraces\":0,\"traceAddress\":[],\"type\":\"reward\"}],\"id\":67}", serialized); + Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xa1e0e640b433d5a8931881b8eee7b1a125474b04e430c0bf8afff52584c53273\",\"transactionPosition\":0,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x5cf5d4a0a93000beb1cfb373508ce4c0153ab491be99b3c927f482346c86a0e1\",\"transactionPosition\":1,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x02d2cde9120e37722f607771ebaa0d4e98c5d99a8a9e7df6872e8c8c9f5c0bc5\",\"transactionPosition\":2,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xe50a2a2d170011b1f9ee080c3810bed0c63dbb1b2b2c541c78ada5b222cc3fd2\",\"transactionPosition\":3,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xff0d4524d379fc15c41a9b0444b943e1a530779b7d09c8863858267c5ef92b24\",\"transactionPosition\":4,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xf9b69366c82084e3799dc4a7ad87dc173ef4923d853bc250de86b81786f2972a\",\"transactionPosition\":5,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x28171c29b23cd96f032fe43f444402af4555ee5f074d5d0d0a1089d940f136e7\",\"transactionPosition\":6,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x09b01caf4b7ecfe9d02251b2e478f2da0fdf08412e3fa1ff963fa80635dab031\",\"transactionPosition\":7,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xd82382905afbe4ca4c2b8e54cea43818c91e0014c3827e3020fbd82b732b8239\",\"transactionPosition\":8,\"type\":\"call\"},{\"action\":{\"author\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"rewardType\":\"block\",\"value\":\"0x1bc16d674ec80000\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"subtraces\":0,\"traceAddress\":[],\"type\":\"reward\"}],\"id\":67}", serialized); } [Test] @@ -152,7 +152,7 @@ public async Task Trace_filter_return_expected_json() EthModuleFactory.Converters.Union(TraceModuleFactory.Converters).ToList(), context.TraceRpcModule, "trace_filter", new EthereumJsonSerializer().Serialize(traceFilterRequest)); - Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xa1e0e640b433d5a8931881b8eee7b1a125474b04e430c0bf8afff52584c53273\",\"transactionPosition\":0,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x5cf5d4a0a93000beb1cfb373508ce4c0153ab491be99b3c927f482346c86a0e1\",\"transactionPosition\":1,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x02d2cde9120e37722f607771ebaa0d4e98c5d99a8a9e7df6872e8c8c9f5c0bc5\",\"transactionPosition\":2,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xe50a2a2d170011b1f9ee080c3810bed0c63dbb1b2b2c541c78ada5b222cc3fd2\",\"transactionPosition\":3,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xff0d4524d379fc15c41a9b0444b943e1a530779b7d09c8863858267c5ef92b24\",\"transactionPosition\":4,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xf9b69366c82084e3799dc4a7ad87dc173ef4923d853bc250de86b81786f2972a\",\"transactionPosition\":5,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x28171c29b23cd96f032fe43f444402af4555ee5f074d5d0d0a1089d940f136e7\",\"transactionPosition\":6,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x09b01caf4b7ecfe9d02251b2e478f2da0fdf08412e3fa1ff963fa80635dab031\",\"transactionPosition\":7,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xd82382905afbe4ca4c2b8e54cea43818c91e0014c3827e3020fbd82b732b8239\",\"transactionPosition\":8,\"type\":\"call\"},{\"action\":{\"author\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"rewardType\":\"block\",\"value\":\"0x1bc16d674ec80000\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":null,\"subtraces\":0,\"traceAddress\":[],\"type\":\"reward\"}],\"id\":67}", serialized, serialized.Replace("\"", "\\\"")); + Assert.AreEqual("{\"jsonrpc\":\"2.0\",\"result\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xa1e0e640b433d5a8931881b8eee7b1a125474b04e430c0bf8afff52584c53273\",\"transactionPosition\":0,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x5cf5d4a0a93000beb1cfb373508ce4c0153ab491be99b3c927f482346c86a0e1\",\"transactionPosition\":1,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x02d2cde9120e37722f607771ebaa0d4e98c5d99a8a9e7df6872e8c8c9f5c0bc5\",\"transactionPosition\":2,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xe50a2a2d170011b1f9ee080c3810bed0c63dbb1b2b2c541c78ada5b222cc3fd2\",\"transactionPosition\":3,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xff0d4524d379fc15c41a9b0444b943e1a530779b7d09c8863858267c5ef92b24\",\"transactionPosition\":4,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xf9b69366c82084e3799dc4a7ad87dc173ef4923d853bc250de86b81786f2972a\",\"transactionPosition\":5,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x28171c29b23cd96f032fe43f444402af4555ee5f074d5d0d0a1089d940f136e7\",\"transactionPosition\":6,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0x09b01caf4b7ecfe9d02251b2e478f2da0fdf08412e3fa1ff963fa80635dab031\",\"transactionPosition\":7,\"type\":\"call\"},{\"action\":{\"callType\":\"call\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"gas\":\"0x0\",\"input\":\"0x\",\"to\":\"0x0000000000000000000000000000000000000000\",\"value\":\"0x1\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[],\"transactionHash\":\"0xd82382905afbe4ca4c2b8e54cea43818c91e0014c3827e3020fbd82b732b8239\",\"transactionPosition\":8,\"type\":\"call\"},{\"action\":{\"author\":\"0x475674cb523a0a2736b7f7534390288fce16982c\",\"rewardType\":\"block\",\"value\":\"0x1bc16d674ec80000\"},\"blockHash\":\"0xcd3d2c10309822aec4cbbfa80ba905ba1de62834a3b40f8012520734db2763ca\",\"blockNumber\":15,\"subtraces\":0,\"traceAddress\":[],\"type\":\"reward\"}],\"id\":67}", serialized, serialized.Replace("\"", "\\\"")); } [Test] @@ -534,7 +534,7 @@ public async Task Trace_transaction_with_error_reverted() traces.Data.ElementAt(0).TransactionHash.Should().Be(transaction2.Hash!); string serialized = new EthereumJsonSerializer().Serialize(traces.Data); - Assert.AreEqual("[{\"action\":{\"traceAddress\":[],\"callType\":\"create\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"create\",\"creationMethod\":\"create\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"to\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"gas\":\"0x9a6c\",\"value\":\"0x1\",\"input\":\"0x60006000600060006000736b5887043de753ecfa6269f947129068263ffbe261c350f160006000600060006000736b5887043de753ecfa6269f947129068263ffbe261c350f1fd\",\"subtraces\":[{\"traceAddress\":[0],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x2dcd\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]},{\"traceAddress\":[1],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x2d56\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]}],\"error\":\"Reverted\"},\"blockHash\":\"0xfa74e932520ee416ecb12171c115b3ad14112ffd2d612646ceaff69e54e06a94\",\"blockNumber\":18,\"result\":null,\"subtraces\":2,\"traceAddress\":[],\"transactionHash\":\"0x787616b8756424622f162fc3817331517ef941366f28db452defc0214bc36b22\",\"transactionPosition\":0,\"type\":\"create\",\"error\":\"Reverted\"},{\"action\":{\"traceAddress\":[0],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x2dcd\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]},\"blockHash\":\"0xfa74e932520ee416ecb12171c115b3ad14112ffd2d612646ceaff69e54e06a94\",\"blockNumber\":18,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[0],\"transactionHash\":\"0x787616b8756424622f162fc3817331517ef941366f28db452defc0214bc36b22\",\"transactionPosition\":0,\"type\":\"call\"},{\"action\":{\"traceAddress\":[1],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x2d56\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]},\"blockHash\":\"0xfa74e932520ee416ecb12171c115b3ad14112ffd2d612646ceaff69e54e06a94\",\"blockNumber\":18,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[1],\"transactionHash\":\"0x787616b8756424622f162fc3817331517ef941366f28db452defc0214bc36b22\",\"transactionPosition\":0,\"type\":\"call\"}]", serialized, serialized.Replace("\"", "\\\"")); + Assert.AreEqual("[{\"action\":{\"traceAddress\":[],\"callType\":\"create\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"create\",\"creationMethod\":\"create\",\"from\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"to\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"gas\":\"0x9a6c\",\"value\":\"0x1\",\"input\":\"0x60006000600060006000736b5887043de753ecfa6269f947129068263ffbe261c350f160006000600060006000736b5887043de753ecfa6269f947129068263ffbe261c350f1fd\",\"subtraces\":[{\"traceAddress\":[0],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x2dcd\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]},{\"traceAddress\":[1],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x2d56\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]}],\"error\":\"Reverted\"},\"blockHash\":\"0xfa74e932520ee416ecb12171c115b3ad14112ffd2d612646ceaff69e54e06a94\",\"blockNumber\":18,\"subtraces\":2,\"traceAddress\":[],\"transactionHash\":\"0x787616b8756424622f162fc3817331517ef941366f28db452defc0214bc36b22\",\"transactionPosition\":0,\"type\":\"create\",\"error\":\"Reverted\"},{\"action\":{\"traceAddress\":[0],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x2dcd\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]},\"blockHash\":\"0xfa74e932520ee416ecb12171c115b3ad14112ffd2d612646ceaff69e54e06a94\",\"blockNumber\":18,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[0],\"transactionHash\":\"0x787616b8756424622f162fc3817331517ef941366f28db452defc0214bc36b22\",\"transactionPosition\":0,\"type\":\"call\"},{\"action\":{\"traceAddress\":[1],\"callType\":\"call\",\"includeInTrace\":true,\"isPrecompiled\":false,\"type\":\"call\",\"from\":\"0xd6a48bcd4c5ad5adacfab677519c25ce7b2805a5\",\"to\":\"0x6b5887043de753ecfa6269f947129068263ffbe2\",\"gas\":\"0x2d56\",\"value\":\"0x0\",\"input\":\"0x\",\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":[]},\"blockHash\":\"0xfa74e932520ee416ecb12171c115b3ad14112ffd2d612646ceaff69e54e06a94\",\"blockNumber\":18,\"result\":{\"gasUsed\":\"0x0\",\"output\":\"0x\"},\"subtraces\":0,\"traceAddress\":[1],\"transactionHash\":\"0x787616b8756424622f162fc3817331517ef941366f28db452defc0214bc36b22\",\"transactionPosition\":0,\"type\":\"call\"}]", serialized, serialized.Replace("\"", "\\\"")); } [Test] public async Task trace_timeout_is_separate_for_rpc_calls() diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/DbPersistingBlockTracerTests.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/DbPersistingBlockTracerTests.cs new file mode 100644 index 00000000000..4f8562f4089 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/DbPersistingBlockTracerTests.cs @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Text.Json; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Test.Builders; +using Nethermind.Db; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.Logging; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.TraceStore.Tests; + +[Parallelizable(ParallelScope.All)] +public class DbPersistingBlockTracerTests +{ + [Test] + public void saves_traces_to_db() + { + ParityLikeBlockTracer parityTracer = new(ParityTraceTypes.Trace); + MemDb memDb = new(); + ParityLikeTraceSerializer serializer = new(LimboLogs.Instance); + DbPersistingBlockTracer dbPersistingTracer = + new(parityTracer, memDb, serializer, LimboLogs.Instance); + + Transaction transaction = Build.A.Transaction.TestObject; + Block block = Build.A.Block.WithTransactions(transaction).TestObject; + dbPersistingTracer.StartNewBlockTrace(block); + dbPersistingTracer.StartNewTxTrace(transaction); + dbPersistingTracer.EndTxTrace(); + dbPersistingTracer.EndBlockTrace(); + + List? traces = serializer.Deserialize(memDb.Get(block.Hash!)); + traces.Should().BeEquivalentTo(new ParityLikeTxTrace[] { new() { BlockHash = block.Hash, TransactionPosition = 0 } }); + + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/Nethermind.JsonRpc.TraceStore.Tests.csproj b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/Nethermind.JsonRpc.TraceStore.Tests.csproj new file mode 100644 index 00000000000..91e11929d6b --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/Nethermind.JsonRpc.TraceStore.Tests.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + + diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceSerializerTests.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceSerializerTests.cs new file mode 100644 index 00000000000..bfcea492d2c --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceSerializerTests.cs @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.IO; +using FluentAssertions; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.Logging; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.TraceStore.Tests; + +public class TraceSerializerTests +{ + [Test] + public void can_deserialize_deep_graph() + { + List? traces = Deserialize(new ParityLikeTraceSerializer(LimboLogs.Instance)); + traces?.Count.Should().Be(36); + } + + [Test] + public void cant_deserialize_deep_graph() + { + Func?> traces = () => Deserialize(new ParityLikeTraceSerializer(LimboLogs.Instance, 128)); + traces.Should().Throw(); + } + + private List? Deserialize(ITraceSerializer serializer) + { + Type type = GetType(); + using Stream stream = type.Assembly.GetManifestResourceStream(type.Namespace + ".xdai-17600039.json")!; + List? traces = serializer.Deserialize(stream); + return traces; + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceStorePrunerTests.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceStorePrunerTests.cs new file mode 100644 index 00000000000..dfc76405d52 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceStorePrunerTests.cs @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Nethermind.Blockchain; +using Nethermind.Blockchain.Find; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Test.Builders; +using Nethermind.Db; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.Logging; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.TraceStore.Tests; + +[Parallelizable(ParallelScope.All)] +public class TraceStorePrunerTests +{ + [Test] + public async Task prunes_old_blocks() + { + IEnumerable GenerateTraces(MemDb db, BlockTree tree) + { + ParityLikeBlockTracer parityTracer = new(ParityTraceTypes.Trace); + ParityLikeTraceSerializer serializer = new(LimboLogs.Instance); + DbPersistingBlockTracer dbPersistingTracer = + new(parityTracer, db, serializer, LimboLogs.Instance); + + Block? current = tree.Head; + while (current is not null) + { + dbPersistingTracer.StartNewBlockTrace(current); + dbPersistingTracer.StartNewTxTrace(Build.A.Transaction.TestObject); + dbPersistingTracer.EndTxTrace(); + dbPersistingTracer.EndBlockTrace(); + yield return current.Hash!; + current = tree.FindParent(current, BlockTreeLookupOptions.None); + } + } + + void AddNewBlocks(BlockTree tree) + { + Block headPlus1 = Build.A.Block.WithParent(tree.Head!).TestObject; + Block headPlus2 = Build.A.Block.WithParent(headPlus1).TestObject; + Block headPlus3 = Build.A.Block.WithParent(headPlus2).TestObject; + tree.SuggestBlock(headPlus1); + tree.SuggestBlock(headPlus2); + tree.SuggestBlock(headPlus3); + Block[] blocks = { headPlus1, headPlus2, headPlus3 }; + tree.UpdateMainChain(blocks, true); + } + + MemDb memDb = new(); + BlockTree blockTree = Build.A.BlockTree().OfChainLength(5).TestObject; + TraceStorePruner tracePruner = new(blockTree, memDb, 3, LimboLogs.Instance); + List keys = GenerateTraces(memDb, blockTree).ToList(); + keys.Select(k => memDb.Get(k)).Should().NotContain((byte[]?)null); + AddNewBlocks(blockTree); + await Task.Delay(100); + keys.Skip(3).Select(k => memDb.Get(k)).Should().NotContain((byte[]?)null); // too old were not removed + keys.Take(3).Select(k => memDb.Get(k)).Should().OnlyContain(b => b == null); // those were removed + + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceStoreRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceStoreRpcModuleTests.cs new file mode 100644 index 00000000000..b5a3eec71df --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/TraceStoreRpcModuleTests.cs @@ -0,0 +1,171 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using FastEnumUtility; +using FluentAssertions; +using Nethermind.Blockchain.Find; +using Nethermind.Blockchain.Receipts; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Test.Builders; +using Nethermind.Db; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.JsonRpc.Data; +using Nethermind.JsonRpc.Modules.Trace; +using Nethermind.Logging; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.JsonRpc.TraceStore.Tests; + +[Parallelizable(ParallelScope.All)] +public class TraceStoreRpcModuleTests +{ + [Test] + public void trace_call_returns_from_inner_module() + { + TestContext test = new(); + + test.Module.trace_call(new TransactionForRpc(Build.A.Transaction.TestObject), new[] { ParityTraceTypes.Trace.ToString() }, BlockParameter.Latest) + .Should().BeEquivalentTo(ResultWrapper.Success(new ParityTxTraceFromReplay(test.NonDbTraces[0]))); + } + + [Test] + public void trace_callMany_returns_from_inner_module() + { + TestContext test = new(); + + TransactionForRpcWithTraceTypes[] calls = { new() { TraceTypes = new[] { ParityTraceTypes.Trace.ToString() }, Transaction = new TransactionForRpc(Build.A.Transaction.TestObject) } }; + test.Module.trace_callMany(calls, BlockParameter.Latest) + .Should().BeEquivalentTo(ResultWrapper>.Success(test.NonDbTraces.Select(t => new ParityTxTraceFromReplay(t)))); + } + + [Test] + public void trace_Transaction_returns_from_inner_module() + { + TestContext test = new(); + + test.Module.trace_rawTransaction(Bytes.Empty, new[] { ParityTraceTypes.Trace.ToString() }) + .Should().BeEquivalentTo(ResultWrapper.Success(new ParityTxTraceFromReplay(test.NonDbTraces[0]))); + } + + [Test] + public void trace_replayTransaction_returns_from_inner_module() + { + TestContext test = new(); + + test.Module.trace_replayTransaction(test.NonDbTraces.First().TransactionHash!, new[] { ParityTraceTypes.Trace.ToString() }) + .Should().BeEquivalentTo(ResultWrapper.Success(new ParityTxTraceFromReplay(test.NonDbTraces[0]))); + } + + [Test] + public void trace_replayTransaction_returns_from_store() + { + TestContext test = new(); + + test.Module.trace_replayTransaction(test.DbTrace.TransactionHash!, new[] { ParityTraceTypes.Trace.ToString() }) + .Should().BeEquivalentTo(ResultWrapper.Success(new ParityTxTraceFromReplay(test.DbTrace))); + } + + [Test] + public void trace_replayBlockTransactions_returns_from_inner_module() + { + TestContext test = new(); + + test.Module.trace_replayBlockTransactions(new BlockParameter(1), new[] { ParityTraceTypes.Trace.ToString() }) + .Should().BeEquivalentTo(ResultWrapper>.Success(test.NonDbTraces.Select(t => new ParityTxTraceFromReplay(t)))); + } + + [Test] + public void trace_replayBlockTransactions_returns_from_store() + { + TestContext test = new(); + + test.Module.trace_replayBlockTransactions(BlockParameter.Latest, new[] { ParityTraceTypes.Trace.ToString(), ParityTraceTypes.Rewards.ToString() }) + .Should().BeEquivalentTo(ResultWrapper>.Success(test.DbTraces.Select(t => new ParityTxTraceFromReplay(t)))); + } + + [Test] + public void trace_filter_returns_from_inner_module() + { + TestContext test = new(); + + test.Module.trace_filter(new TraceFilterForRpc { FromBlock = new BlockParameter(1), ToBlock = new BlockParameter(1) }) + .Should().BeEquivalentTo(ResultWrapper>.Success(test.NonDbTraces.SelectMany(ParityTxTraceFromStore.FromTxTrace))); + } + + [Test] + public void trace_filter_returns_from_store() + { + TestContext test = new(); + + test.Module.trace_filter(new TraceFilterForRpc { FromBlock = BlockParameter.Latest, ToBlock = BlockParameter.Latest }) + .Should().BeEquivalentTo(ResultWrapper>.Success(test.DbTraces.SelectMany(ParityTxTraceFromStore.FromTxTrace))); + } + + private class TestContext + { + public ParityLikeTxTrace DbTrace { get; } + public ParityLikeTxTrace[] DbTraces { get; } + public ParityLikeTxTrace[] NonDbTraces { get; } + public ITraceRpcModule InnerModule { get; } + public MemDb Store { get; } + public IBlockFinder BlockFinder { get; } + public IReceiptFinder ReceiptFinder { get; } + public TraceStoreRpcModule Module { get; } + + public TestContext() + { + InnerModule = Substitute.For(); + Store = new MemDb(); + BlockFinder = Build.A.BlockTree().OfChainLength(3).TestObject; + ReceiptFinder = Substitute.For(); + ParityLikeTraceSerializer serializer = new(LimboLogs.Instance); + Module = new TraceStoreRpcModule(InnerModule, Store, BlockFinder, ReceiptFinder, serializer, LimboLogs.Instance); + Keccak dbTransaction = Build.A.Transaction.TestObject.Hash!; + Keccak dbBlock = BlockFinder.Head!.Hash!; + DbTrace = new() { BlockHash = dbBlock, TransactionHash = dbTransaction }; + DbTraces = new[] { DbTrace }; + Keccak nonDbTransaction = TestItem.KeccakA; + NonDbTraces = new[] { new ParityLikeTxTrace() { BlockHash = dbBlock, TransactionHash = nonDbTransaction } }; + Store.Set(dbBlock, serializer.Serialize(DbTraces)); + ReceiptFinder.FindBlockHash(dbTransaction).Returns(dbBlock); + ReceiptFinder.FindBlockHash(nonDbTransaction).Returns(dbBlock); + + ResultWrapper nonDbReplayWrapper = ResultWrapper.Success(new(NonDbTraces[0])); + ResultWrapper> nonDbReplaysWrapper = ResultWrapper>.Success(NonDbTraces.Select(t => new ParityTxTraceFromReplay(t))); + + InnerModule.trace_call(Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(nonDbReplayWrapper); + + InnerModule.trace_callMany(Arg.Any(), Arg.Any()) + .Returns(nonDbReplaysWrapper); + + InnerModule.trace_rawTransaction(Arg.Any(), Arg.Any()) + .Returns(nonDbReplayWrapper); + + InnerModule.trace_replayTransaction(nonDbTransaction, Arg.Any()) + .Returns(nonDbReplayWrapper); + + InnerModule.trace_replayBlockTransactions(Arg.Any(), Arg.Any()) + .Returns(nonDbReplaysWrapper); + + ResultWrapper> nonDbFromStoreWrapper = ResultWrapper>.Success(NonDbTraces.SelectMany(ParityTxTraceFromStore.FromTxTrace)); + InnerModule.trace_filter(Arg.Any()) + .Returns(nonDbFromStoreWrapper); + + InnerModule.trace_block(BlockParameter.Latest) + .Returns(nonDbFromStoreWrapper); + + InnerModule.trace_get(nonDbTransaction, new[] { 0L }) + .Returns(nonDbFromStoreWrapper); + + InnerModule.trace_transaction(nonDbTransaction) + .Returns(nonDbFromStoreWrapper); + + } + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/xdai-17600039.json b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/xdai-17600039.json new file mode 100644 index 00000000000..3bdc7edef35 Binary files /dev/null and b/src/Nethermind/Nethermind.JsonRpc.TraceStore.Tests/xdai-17600039.json differ diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/DbPersistingBlockTracer.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/DbPersistingBlockTracer.cs new file mode 100644 index 00000000000..af122fb5495 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/DbPersistingBlockTracer.cs @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Db; +using Nethermind.Evm.Tracing; +using Nethermind.Int256; +using Nethermind.Logging; + +namespace Nethermind.JsonRpc.TraceStore; + +/// +/// Tracer that can store traces of decorated tracer in database +/// +/// Trace type +/// Transaction tracer type +public class DbPersistingBlockTracer : IBlockTracer where TTracer : class, ITxTracer +{ + private readonly IDb _db; + private readonly ITraceSerializer _traceSerializer; + private readonly IBlockTracer _blockTracer; + private readonly BlockTracerBase _tracerWithResults; + private Keccak _currentBlockHash = null!; + private long _currentBlockNumber; + private readonly ILogger _logger; + + /// + /// Creates the tracer + /// + /// Internal, actual tracer that does the tracing + /// Database + /// Serializer + /// + public DbPersistingBlockTracer(BlockTracerBase blockTracer, + IDb db, + ITraceSerializer traceSerializer, + ILogManager logManager) + { + _db = db; + _traceSerializer = traceSerializer; + _blockTracer = _tracerWithResults = blockTracer; + _logger = logManager.GetClassLogger>(); + } + + public bool IsTracingRewards => _blockTracer.IsTracingRewards; + + public void ReportReward(Address author, string rewardType, UInt256 rewardValue) => + _blockTracer.ReportReward(author, rewardType, rewardValue); + + public void StartNewBlockTrace(Block block) + { + _currentBlockHash = block.Hash!; + _currentBlockNumber = block.Number; + _blockTracer.StartNewBlockTrace(block); + } + + public ITxTracer StartNewTxTrace(Transaction? tx) => _blockTracer.StartNewTxTrace(tx); + + public void EndTxTrace() => _blockTracer.EndTxTrace(); + + public void EndBlockTrace() + { + _blockTracer.EndBlockTrace(); + IReadOnlyCollection result = _tracerWithResults.BuildResult(); + byte[] tracesSerialized = _traceSerializer.Serialize(result); + Keccak currentBlockHash = _currentBlockHash; + long currentBlockNumber = _currentBlockNumber; + _db.Set(currentBlockHash, tracesSerialized); + if (_logger.IsTrace) _logger.Trace($"Saved traces for block {currentBlockNumber} ({currentBlockHash}) with size {tracesSerialized.Length} bytes for {result.Count} traces."); + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/ITraceSerializer.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/ITraceSerializer.cs new file mode 100644 index 00000000000..646376a0479 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/ITraceSerializer.cs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Evm.Tracing.ParityStyle; + +namespace Nethermind.JsonRpc.TraceStore; + +public interface ITraceSerializer +{ + unsafe List? Deserialize(Span serialized); + List? Deserialize(Stream serialized); + byte[] Serialize(IReadOnlyCollection traces); +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/ITraceStoreConfig.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/ITraceStoreConfig.cs new file mode 100644 index 00000000000..d2a81a9821d --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/ITraceStoreConfig.cs @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Config; +using Nethermind.Evm.Tracing.ParityStyle; + +namespace Nethermind.JsonRpc.TraceStore; + +public interface ITraceStoreConfig : IConfig +{ + [ConfigItem(Description = "Defines whether the TraceStore plugin is enabled, if 'true' traces will come from DB if possible.", DefaultValue = "false")] + public bool Enabled { get; set; } + + [ConfigItem(Description = "Defines how many blocks counting from head are kept in the TraceStore, if '0' all traces of processed blocks will be kept.", DefaultValue = "10000")] + public int BlocksToKeep { get; set; } + + [ConfigItem(Description = "Defines what kind of traces are saved and kept in TraceStore. Available options are: Trace, Rewards, VmTrace, StateDiff or just All.", DefaultValue = "Trace, Rewards")] + public ParityTraceTypes TraceTypes { get; set; } + + [ConfigItem(Description = "Verifies all the serialized elements.", DefaultValue = "false", HiddenFromDocs = true)] + bool VerifySerialized { get; set; } + + [ConfigItem(Description = "Depth to deserialize traces.", DefaultValue = "1024", HiddenFromDocs = true)] + int MaxDepth { get; set; } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/Nethermind.JsonRpc.TraceStore.csproj b/src/Nethermind/Nethermind.JsonRpc.TraceStore/Nethermind.JsonRpc.TraceStore.csproj new file mode 100644 index 00000000000..da0330aae89 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/Nethermind.JsonRpc.TraceStore.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + true + + + + + + + diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/ParityLikeTraceSerializer.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/ParityLikeTraceSerializer.cs new file mode 100644 index 00000000000..806efddda33 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/ParityLikeTraceSerializer.cs @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.IO.Compression; +using Nethermind.Core.Collections; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.JsonRpc.Modules.Trace; +using Nethermind.Logging; +using Nethermind.Serialization.Json; + +namespace Nethermind.JsonRpc.TraceStore; + +public class ParityLikeTraceSerializer : ITraceSerializer +{ + private static readonly byte[] _emptyBytes = { 0 }; + private static readonly List _emptyTraces = new(); + + private readonly ILogger? _logger; + private readonly IJsonSerializer _jsonSerializer; + private readonly bool _verifySerialized; + + public ParityLikeTraceSerializer(ILogManager logManager, int maxDepth = 1024, bool verifySerialized = false) + { + _jsonSerializer = new EthereumJsonSerializer(maxDepth, new ParityTraceActionCreationConverter()); + _verifySerialized = verifySerialized; + _logger = logManager?.GetClassLogger(); + } + + public unsafe List? Deserialize(Span serialized) + { + if (serialized.Length == 1) return _emptyTraces; + + fixed (byte* pBuffer = &serialized[0]) + { + using UnmanagedMemoryStream input = new(pBuffer, serialized.Length); + return Deserialize(input); + } + } + + public List? Deserialize(Stream serialized) + { + using GZipStream compressionStream = new(serialized, CompressionMode.Decompress); + return _jsonSerializer.Deserialize>(compressionStream); + } + + public byte[] Serialize(IReadOnlyCollection traces) + { + if (traces.Count == 0) return _emptyBytes; + + using MemoryStream output = new(); + using (GZipStream compressionStream = new(output, CompressionMode.Compress)) + { + _jsonSerializer.Serialize(compressionStream, traces); + } + + byte[] result = output.ToArray(); + + // This is for testing + if (_verifySerialized) + { + Task.Run(() => + { + try + { + Deserialize(result); + } + catch (Exception e) + { + ParityLikeTxTrace? trace = traces.FirstOrDefault(); + string tracesWrittenToPath = Path.Combine(Path.GetTempPath(), $"{trace?.BlockNumber}-{trace?.BlockHash}.zip"); + if (_logger?.IsError == true) _logger.Error($"Can't deserialize trace logs for block {trace?.BlockNumber} ({trace?.BlockHash}), size {result.Length}, dump: {tracesWrittenToPath}", e); + File.WriteAllBytes(tracesWrittenToPath, result); + } + }); + } + + return result; + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/ParityTraceActionCreationConverter.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/ParityTraceActionCreationConverter.cs new file mode 100644 index 00000000000..b5af77d7ac2 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/ParityTraceActionCreationConverter.cs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Evm.Tracing.ParityStyle; +using Newtonsoft.Json.Converters; + +namespace Nethermind.JsonRpc.TraceStore; + +public class ParityTraceActionCreationConverter : CustomCreationConverter +{ + public override ParityTraceAction Create(Type objectType) => new() { Result = null }; +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreConfig.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreConfig.cs new file mode 100644 index 00000000000..17cb9dafb8e --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreConfig.cs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Evm.Tracing.ParityStyle; + +namespace Nethermind.JsonRpc.TraceStore; + +public class TraceStoreConfig : ITraceStoreConfig +{ + public bool Enabled { get; set; } + public int BlocksToKeep { get; set; } = 10000; + public ParityTraceTypes TraceTypes { get; set; } = ParityTraceTypes.Trace | ParityTraceTypes.Rewards; + public bool VerifySerialized { get; set; } = false; + public int MaxDepth { get; set; } = 1024; +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreModuleFactory.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreModuleFactory.cs new file mode 100644 index 00000000000..408878a85c7 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreModuleFactory.cs @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain.Find; +using Nethermind.Blockchain.Receipts; +using Nethermind.Db; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.JsonRpc.Modules; +using Nethermind.JsonRpc.Modules.Trace; +using Nethermind.Logging; + +namespace Nethermind.JsonRpc.TraceStore; + +public class TraceStoreModuleFactory : ModuleFactoryBase +{ + private readonly IRpcModuleFactory _innerFactory; + private readonly IDbWithSpan _traceStore; + private readonly IBlockFinder _blockFinder; + private readonly IReceiptFinder _receiptFinder; + private readonly ITraceSerializer _traceSerializer; + private readonly ILogManager _logManager; + + public TraceStoreModuleFactory( + IRpcModuleFactory innerFactory, + IDbWithSpan traceStore, + IBlockFinder blockFinder, + IReceiptFinder receiptFinder, + ITraceSerializer traceSerializer, + ILogManager logManager) + { + _innerFactory = innerFactory; + _traceStore = traceStore; + _blockFinder = blockFinder; + _receiptFinder = receiptFinder; + _traceSerializer = traceSerializer; + _logManager = logManager; + } + + public override ITraceRpcModule Create() => new TraceStoreRpcModule(_innerFactory.Create(), _traceStore, _blockFinder, _receiptFinder, _traceSerializer, _logManager); +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStorePlugin.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStorePlugin.cs new file mode 100644 index 00000000000..acfd0351b20 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStorePlugin.cs @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json; +using FastEnumUtility; +using Nethermind.Api; +using Nethermind.Api.Extensions; +using Nethermind.Blockchain.Find; +using Nethermind.Db; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.JsonRpc.Modules; +using Nethermind.JsonRpc.Modules.Trace; +using Nethermind.Logging; + +namespace Nethermind.JsonRpc.TraceStore; + +public class TraceStorePlugin : INethermindPlugin +{ + private const string DbName = "TraceStore"; + private INethermindApi _api = null!; + private ITraceStoreConfig _config = null!; + private IJsonRpcConfig _jsonRpcConfig = null!; + private IDbWithSpan? _db; + private TraceStorePruner? _pruner; + private ILogManager _logManager = null!; + private ILogger _logger = null!; + private ITraceSerializer? _traceSerializer; + public string Name => DbName; + public string Description => "Allows to serve traces without the block state, by saving historical traces to DB."; + public string Author => "Nethermind"; + private bool Enabled => _config.Enabled; + + public Task Init(INethermindApi nethermindApi) + { + _api = nethermindApi; + _logManager = _api.LogManager; + _config = _api.Config(); + _jsonRpcConfig = _api.Config(); + _logger = _logManager.GetClassLogger(); + + if (Enabled) + { + // Setup serialization + _traceSerializer = new ParityLikeTraceSerializer(_logManager, _config.MaxDepth, _config.VerifySerialized); + + // Setup DB + _db = (IDbWithSpan)_api.RocksDbFactory!.CreateDb(new RocksDbSettings(DbName, DbName.ToLower())); + _api.DbProvider!.RegisterDb(DbName, _db); + + //Setup pruning if configured + if (_config.BlocksToKeep != 0) + { + _pruner = new TraceStorePruner(_api.BlockTree!, _db, _config.BlocksToKeep, _logManager); + } + } + + return Task.CompletedTask; + } + + public Task InitNetworkProtocol() + { + if (Enabled) + { + if (_logger.IsInfo) _logger.Info($"Starting TraceStore with {_config.TraceTypes} traces."); + + // Setup tracing + ParityLikeBlockTracer parityTracer = new(_config.TraceTypes); + DbPersistingBlockTracer dbPersistingTracer = + new(parityTracer, _db!, _traceSerializer!, _logManager); + _api.BlockchainProcessor!.Tracers.Add(dbPersistingTracer); + } + + // Potentially we could add protocol for syncing traces. + return Task.CompletedTask; + } + + public Task InitRpcModules() + { + if (Enabled && _jsonRpcConfig.Enabled) + { + IRpcModuleProvider apiRpcModuleProvider = _api.RpcModuleProvider!; + if (apiRpcModuleProvider.GetPool(ModuleType.Trace) is IRpcModulePool traceModulePool) + { + TraceStoreModuleFactory traceModuleFactory = new(traceModulePool.Factory, _db!, _api.BlockTree!, _api.ReceiptFinder!, _traceSerializer!, _logManager); + apiRpcModuleProvider.RegisterBoundedByCpuCount(traceModuleFactory, _jsonRpcConfig.Timeout); + } + } + + return Task.CompletedTask; + } + + public ValueTask DisposeAsync() + { + if (Enabled) + { + _pruner?.Dispose(); + _db?.Dispose(); + } + + return default; + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStorePruner.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStorePruner.cs new file mode 100644 index 00000000000..ce4091abf40 --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStorePruner.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain; +using Nethermind.Core; +using Nethermind.Db; +using Nethermind.Logging; + +namespace Nethermind.JsonRpc.TraceStore; + +/// +/// Prunes tracing history +/// +public class TraceStorePruner : IDisposable +{ + private readonly IBlockTree _blockTree; + private readonly IDb _db; + private readonly int _blockToKeep; + private readonly ILogger _logger; + + public TraceStorePruner(IBlockTree blockTree, IDb db, int blockToKeep, ILogManager logManager) + { + _blockTree = blockTree; + _db = db; + _blockToKeep = blockToKeep; + _logger = logManager.GetClassLogger(); + _blockTree.BlockAddedToMain += OnBlockAddedToMain; + if (_logger.IsDebug) _logger.Debug($"TraceStore pruning is enabled, keeping last {blockToKeep} blocks."); + } + + private void OnBlockAddedToMain(object? sender, BlockReplacementEventArgs e) + { + Task.Run((() => + { + long levelToDelete = e.Block.Number - _blockToKeep; + if (levelToDelete > 0) + { + ChainLevelInfo? level = _blockTree.FindLevel(levelToDelete); + if (level is not null) + { + for (int i = 0; i < level.BlockInfos.Length; i++) + { + BlockInfo blockInfo = level.BlockInfos[i]; + if (_logger.IsTrace) _logger.Trace($"Removing traces from TraceStore on level {levelToDelete} for block {blockInfo}."); + _db.Delete(blockInfo.BlockHash); + } + } + } + })); + } + + public void Dispose() + { + _blockTree.BlockAddedToMain -= OnBlockAddedToMain; + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs new file mode 100644 index 00000000000..6bc6ee1e54b --- /dev/null +++ b/src/Nethermind/Nethermind.JsonRpc.TraceStore/TraceStoreRpcModule.cs @@ -0,0 +1,311 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json; +using Nethermind.Blockchain.Find; +using Nethermind.Blockchain.Receipts; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Db; +using Nethermind.Evm.Tracing.ParityStyle; +using Nethermind.JsonRpc.Data; +using Nethermind.JsonRpc.Modules; +using Nethermind.JsonRpc.Modules.Trace; +using Nethermind.Logging; + +namespace Nethermind.JsonRpc.TraceStore; + +/// +/// Module for tracing using database +/// +public class TraceStoreRpcModule : ITraceRpcModule +{ + private readonly IDbWithSpan _traceStore; + private readonly ITraceRpcModule _traceModule; + private readonly IBlockFinder _blockFinder; + private readonly IReceiptFinder _receiptFinder; + private readonly ITraceSerializer _traceSerializer; + private readonly ILogger _logger; + + private static readonly IDictionary> _filters = new Dictionary> + { + { ParityTraceTypes.Trace, FilterTrace }, + { ParityTraceTypes.StateDiff , FilterStateDiff }, + { ParityTraceTypes.VmTrace | ParityTraceTypes.Trace, FilterStateVmTrace } + }; + + public TraceStoreRpcModule( + ITraceRpcModule traceModule, + IDbWithSpan traceStore, + IBlockFinder blockFinder, + IReceiptFinder receiptFinder, + ITraceSerializer traceSerializer, + ILogManager logManager) + { + _traceStore = traceStore; + _traceModule = traceModule; + _blockFinder = blockFinder; + _receiptFinder = receiptFinder; + _traceSerializer = traceSerializer; + _logger = logManager.GetClassLogger(); + } + + public ResultWrapper trace_call(TransactionForRpc call, string[] traceTypes, BlockParameter? blockParameter = null) => + _traceModule.trace_call(call, traceTypes, blockParameter); + + public ResultWrapper> trace_callMany(TransactionForRpcWithTraceTypes[] calls, BlockParameter? blockParameter = null) => + _traceModule.trace_callMany(calls, blockParameter); + + public ResultWrapper trace_rawTransaction(byte[] data, string[] traceTypes) => + _traceModule.trace_rawTransaction(data, traceTypes); + + public ResultWrapper trace_replayTransaction(Keccak txHash, params string[] traceTypes) => + TryTraceTransaction( + txHash, + TraceRpcModule.GetParityTypes(traceTypes), + static t => new ParityTxTraceFromReplay(t), + out ResultWrapper? result) + && result is not null + ? result + : _traceModule.trace_replayTransaction(txHash, traceTypes); + + public ResultWrapper> trace_replayBlockTransactions(BlockParameter blockParameter, string[] traceTypes) + { + SearchResult blockSearch = _blockFinder.SearchForHeader(blockParameter); + if (blockSearch.IsError) + { + return ResultWrapper>.Fail(blockSearch); + } + + BlockHeader block = blockSearch.Object!; + + if (TryGetBlockTraces(block, out List? traces) && traces is not null) + { + FilterTraces(traces, TraceRpcModule.GetParityTypes(traceTypes)); + return ResultWrapper>.Success(traces.Select(t => new ParityTxTraceFromReplay(t, true))); + } + + return _traceModule.trace_replayBlockTransactions(blockParameter, traceTypes); + } + + public ResultWrapper> trace_filter(TraceFilterForRpc traceFilterForRpc) + { + IEnumerable> blocksSearch = _blockFinder.SearchForBlocksOnMainChain( + traceFilterForRpc.FromBlock ?? BlockParameter.Latest, + traceFilterForRpc.ToBlock ?? BlockParameter.Latest); + + SearchResult? error = null; + bool missingTraces = false; + + IEnumerable txTraces = blocksSearch.AsParallel() + .AsOrdered() + .SelectMany(blockSearch => + { + if (blockSearch.IsError) + { + error = blockSearch; + return Enumerable.Empty(); + } + + Block block = blockSearch.Object!; + if (TryGetBlockTraces(block.Header, out List? traces) && traces is not null) + { + return traces.SelectMany(ParityTxTraceFromStore.FromTxTrace); + } + else + { + missingTraces = true; + return Enumerable.Empty(); + } + }); + + if (error is not null) + { + return ResultWrapper>.Fail(error.Value); + } + else if (missingTraces) + { + // fallback when we miss any traces in db + return _traceModule.trace_filter(traceFilterForRpc); + } + else + { + TxTraceFilter txTracerFilter = new(traceFilterForRpc.FromAddress, traceFilterForRpc.ToAddress, traceFilterForRpc.After, traceFilterForRpc.Count); + return ResultWrapper>.Success(txTracerFilter.FilterTxTraces(txTraces)); + } + } + + public ResultWrapper> trace_block(BlockParameter blockParameter) + { + SearchResult blockSearch = _blockFinder.SearchForHeader(blockParameter); + if (blockSearch.IsError) + { + return ResultWrapper>.Fail(blockSearch); + } + + BlockHeader block = blockSearch.Object!; + if (TryGetBlockTraces(block, out List? traces) && traces is not null) + { + return ResultWrapper>.Success(traces.SelectMany(ParityTxTraceFromStore.FromTxTrace)); + } + + return _traceModule.trace_block(blockParameter); + } + + public ResultWrapper> trace_get(Keccak txHash, long[] positions) + { + ResultWrapper> traceTransaction = trace_transaction(txHash); + List traces = TraceRpcModule.ExtractPositionsFromTxTrace(positions, traceTransaction); + return ResultWrapper>.Success(traces); + } + + public ResultWrapper> trace_transaction(Keccak txHash) => + TryTraceTransaction( + txHash, + ParityTraceTypes.Trace, + static t => ParityTxTraceFromStore.FromTxTrace(t), + out ResultWrapper>? result) + && result is not null + ? result + : _traceModule.trace_transaction(txHash); + + private bool TryTraceTransaction( + Keccak txHash, + ParityTraceTypes traceTypes, + Func map, + out ResultWrapper? result) + { + SearchResult blockHashSearch = _receiptFinder.SearchForReceiptBlockHash(txHash); + if (blockHashSearch.IsError) + { + { + result = ResultWrapper.Fail(blockHashSearch); + return true; + } + } + + SearchResult blockSearch = _blockFinder.SearchForBlock(new BlockParameter(blockHashSearch.Object!)); + if (blockSearch.IsError) + { + { + result = ResultWrapper.Fail(blockSearch); + return true; + } + } + + Block block = blockSearch.Object!; + + if (TryGetBlockTraces(block.Header, out List? traces) && traces is not null) + { + ParityLikeTxTrace? trace = GetTxTrace(block, txHash, traces); + if (trace is not null) + { + FilterTrace(trace, traceTypes); + result = ResultWrapper.Success(map(trace)); + return true; + } + } + + result = null; + return false; + } + + private bool TryGetBlockTraces(BlockHeader block, out List? traces) + { + Span tracesSerialized = _traceStore.GetSpan(block.Hash!); + try + { + if (!tracesSerialized.IsEmpty) + { + List? tracesDeserialized = _traceSerializer.Deserialize(tracesSerialized); + if (tracesDeserialized is not null) + { + if (_logger.IsTrace) _logger.Trace($"Found persisted traces for block {block.ToString(BlockHeader.Format.FullHashAndNumber)}"); + traces = tracesDeserialized; + return true; + } + } + + traces = null; + return false; + } + finally + { + _traceStore.DangerousReleaseMemory(tracesSerialized); + } + } + + private ParityLikeTxTrace? GetTxTrace(Block block, Keccak txHash, List traces) + { + int index = traces.FindIndex(t => t.TransactionHash == txHash); + return index != -1 ? traces[index] : null; + } + + private void FilterTraces(List traces, ParityTraceTypes traceTypes) + { + for (int i = 0; i < traces.Count; i++) + { + ParityLikeTxTrace parityLikeTxTrace = traces[i]; + FilterTrace(parityLikeTxTrace, traceTypes); + } + + if ((traceTypes & ParityTraceTypes.Rewards) == 0) + { + FilterRewards(traces); + } + } + + private static void FilterTrace(ParityLikeTxTrace parityLikeTxTrace, ParityTraceTypes traceTypes) + { + foreach (KeyValuePair> filter in _filters) + { + if ((traceTypes & filter.Key) == 0) + { + filter.Value(parityLikeTxTrace); + } + } + } + + + // VmTrace uses flags IsTracingCode, IsTracingInstructions + private static void FilterStateVmTrace(ParityLikeTxTrace trace) + { + trace.VmTrace = null; + } + + // StateDiff uses flags IsTracingState, IsTracingStorage + private static void FilterStateDiff(ParityLikeTxTrace trace) + { + trace.StateChanges = null; + if (trace.VmTrace is not null) + { + for (int i = 0; i < trace.VmTrace.Operations.Length; i++) + { + trace.VmTrace.Operations[i].Store = null!; + } + } + } + + // Trace uses flags IsTracingActions, IsTracingReceipt + private static void FilterTrace(ParityLikeTxTrace trace) + { + trace.Output = null; + // trace action? + } + + private static void FilterRewards(List traces) + { + for (int i = traces.Count - 1; i >= 0; i--) + { + ParityLikeTxTrace trace = traces[i]; + if (trace.TransactionHash is null && trace.Action?.Type == "reward") + { + traces.RemoveAt(i); + } + else + { + break; + } + } + } +} diff --git a/src/Nethermind/Nethermind.JsonRpc/JsonRpcService.cs b/src/Nethermind/Nethermind.JsonRpc/JsonRpcService.cs index 8b9bc8ddfe2..15a6593d82c 100644 --- a/src/Nethermind/Nethermind.JsonRpc/JsonRpcService.cs +++ b/src/Nethermind/Nethermind.JsonRpc/JsonRpcService.cs @@ -197,11 +197,11 @@ private async Task ExecuteAsync(JsonRpcRequest request, string } catch (TargetParameterCountException e) { - return GetErrorResponse(methodName, ErrorCodes.InvalidParams, e.Message, e.Data, request.Id, returnAction); + return GetErrorResponse(methodName, ErrorCodes.InvalidParams, e.Message, e.ToString(), request.Id, returnAction); } catch (TargetInvocationException e) when (e.InnerException is JsonException) { - return GetErrorResponse(methodName, ErrorCodes.InvalidParams, "Invalid params", null, request.Id, returnAction); + return GetErrorResponse(methodName, ErrorCodes.InvalidParams, "Invalid params", e.InnerException?.ToString(), request.Id, returnAction); } catch (Exception e) when (e.InnerException is OperationCanceledException) { @@ -210,7 +210,7 @@ private async Task ExecuteAsync(JsonRpcRequest request, string } catch (Exception e) when (e.InnerException is InsufficientBalanceException) { - return GetErrorResponse(methodName, ErrorCodes.InvalidInput, e.InnerException.Message, null, request.Id, returnAction); + return GetErrorResponse(methodName, ErrorCodes.InvalidInput, e.InnerException.Message, e.ToString(), request.Id, returnAction); } finally { @@ -399,7 +399,7 @@ public JsonRpcErrorResponse GetErrorResponse(int errorCode, string errorMessage) private JsonRpcErrorResponse GetErrorResponse(string? methodName, int errorCode, string? errorMessage, object? errorData, object? id, Action? disposableAction = null) { - if (_logger.IsDebug) _logger.Debug($"Sending error response, method: {methodName ?? "none"}, id: {id}, errorType: {errorCode}, message: {errorMessage}"); + if (_logger.IsDebug) _logger.Debug($"Sending error response, method: {methodName ?? "none"}, id: {id}, errorType: {errorCode}, message: {errorMessage}, errorData: {errorData}"); JsonRpcErrorResponse response = new(disposableAction) { Error = new Error diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/BoundedModulePool.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/BoundedModulePool.cs index fd912488f44..0d02062c663 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/BoundedModulePool.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/BoundedModulePool.cs @@ -55,6 +55,6 @@ public void ReturnModule(T module) _semaphore.Release(); } - public IRpcModuleFactory Factory { get; set; } + public IRpcModuleFactory Factory { get; } } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModulePool.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModulePool.cs index 914d85074ff..4776aab783c 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModulePool.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModulePool.cs @@ -1,16 +1,21 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Threading; using System.Threading.Tasks; namespace Nethermind.JsonRpc.Modules { - public interface IRpcModulePool where T : IRpcModule + public interface IRpcModulePool + { + } + + public interface IRpcModulePool : IRpcModulePool where T : IRpcModule { Task GetModule(bool canBeShared); void ReturnModule(T module); - IRpcModuleFactory Factory { get; set; } + IRpcModuleFactory Factory { get; } } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModuleProvider.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModuleProvider.cs index 3980a68b0e5..6fca4c8b045 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModuleProvider.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModuleProvider.cs @@ -28,5 +28,7 @@ public interface IRpcModuleProvider Task Rent(string methodName, bool canBeShared); void Return(string methodName, IRpcModule rpcModule); + + IRpcModulePool? GetPool(string moduleType); } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/NullModuleProvider.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/NullModuleProvider.cs index bc89edd8b61..c0c2b2e05f4 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/NullModuleProvider.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/NullModuleProvider.cs @@ -30,23 +30,14 @@ public void Register(IRpcModulePool pool) where T : IRpcModule public IReadOnlyCollection All => Array.Empty(); - public ModuleResolution Check(string methodName, JsonRpcContext context) - { - return ModuleResolution.Unknown; - } + public ModuleResolution Check(string methodName, JsonRpcContext context) => ModuleResolution.Unknown; - public (MethodInfo, bool) Resolve(string methodName) - { - return (null, false); - } + public (MethodInfo, bool) Resolve(string methodName) => (null, false); - public Task Rent(string methodName, bool canBeShared) - { - return Null; - } + public Task Rent(string methodName, bool canBeShared) => Null; - public void Return(string methodName, IRpcModule rpcModule) - { - } + public void Return(string methodName, IRpcModule rpcModule) { } + + public IRpcModulePool? GetPool(string moduleType) => null; } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/RpcModuleProvider.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/RpcModuleProvider.cs index bbee59e9876..3e6e8ff890b 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/RpcModuleProvider.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/RpcModuleProvider.cs @@ -18,14 +18,11 @@ public class RpcModuleProvider : IRpcModuleProvider private readonly ILogger _logger; private readonly IJsonRpcConfig _jsonRpcConfig; - private readonly List _modules = new(); - private readonly List _enabledModules = new(); + private readonly HashSet _modules = new(StringComparer.InvariantCultureIgnoreCase); + private readonly HashSet _enabledModules = new(StringComparer.InvariantCultureIgnoreCase); + private readonly Dictionary _methods = new(StringComparer.InvariantCulture); - private readonly Dictionary _methods - = new(StringComparer.InvariantCulture); - - private readonly Dictionary> RentModule, Action ReturnModule)> _pools - = new(); + private readonly Dictionary> RentModule, Action ReturnModule, IRpcModulePool ModulePool)> _pools = new(); private readonly IRpcMethodFilter _filter = NullRpcMethodFilter.Instance; @@ -53,14 +50,13 @@ public void Register(IRpcModulePool pool) where T : IRpcModule RpcModuleAttribute attribute = typeof(T).GetCustomAttribute(); if (attribute is null) { - if (_logger.IsWarn) _logger.Warn( - $"Cannot register {typeof(T).Name} as a JSON RPC module because it does not have a {nameof(RpcModuleAttribute)} applied."); + if (_logger.IsWarn) _logger.Warn($"Cannot register {typeof(T).Name} as a JSON RPC module because it does not have a {nameof(RpcModuleAttribute)} applied."); return; } string moduleType = attribute.ModuleType; - _pools[moduleType] = (async canBeShared => await pool.GetModule(canBeShared), m => pool.ReturnModule((T)m)); + _pools[moduleType] = (async canBeShared => await pool.GetModule(canBeShared), m => pool.ReturnModule((T)m), pool); _modules.Add(moduleType); IReadOnlyCollection poolConverters = pool.Factory.GetConverters(); @@ -117,6 +113,8 @@ public void Return(string methodName, IRpcModule rpcModule) _pools[result.ModuleType].ReturnModule(rpcModule); } + public IRpcModulePool? GetPool(string moduleType) => _pools.TryGetValue(moduleType, out var poolInfo) ? poolInfo.ModulePool : null; + private IDictionary GetMethodDict(Type type) { var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/SingletonModulePool.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/SingletonModulePool.cs index c4d40ec5349..ddef5fb9248 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/SingletonModulePool.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/SingletonModulePool.cs @@ -37,6 +37,6 @@ public void ReturnModule(T module) { } - public IRpcModuleFactory Factory { get; set; } + public IRpcModuleFactory Factory { get; } } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscribeRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscribeRpcModule.cs index 98592b4b3fd..87c723e8c01 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscribeRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscribeRpcModule.cs @@ -1,8 +1,9 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-License-Identifier: LGPL-3.0-only using System; using System.Collections.Generic; +using Newtonsoft.Json; namespace Nethermind.JsonRpc.Modules.Subscribe { @@ -30,6 +31,10 @@ public ResultWrapper eth_subscribe(string subscriptionName, string? args { return ResultWrapper.Fail($"Invalid params", ErrorCodes.InvalidParams, e.Message); } + catch (JsonReaderException) + { + return ResultWrapper.Fail($"Invalid params", ErrorCodes.InvalidParams); + } } public ResultWrapper eth_unsubscribe(string subscriptionId) diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs index b6b06f3753a..5fafb6b8e91 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ITraceRpcModule.cs @@ -31,7 +31,7 @@ public interface ITraceRpcModule : IRpcModule ResultWrapper trace_replayTransaction([JsonRpcParameter(ExampleValue = "[\"0x203abf19610ce15bc509d4b341e907ff8c5a8287ae61186fd4da82146408c28c\",[\"trace\"]]")] Keccak txHash, string[] traceTypes); [JsonRpcMethod(Description = "", IsImplemented = true, IsSharable = false, ExampleResponse = "[{\"output\":\"0x0000000000000000000000000000000000000000000000000000000000000001\",\"stateDiff\":null,\"trace\":[{\"action\":{\"callType\":\"call\",\"from\":\"0x37f207b3ebda37de11ad2b6d306464e313c4841a\",\"gas\":\"0x3c36\",\"input\":\"0xa9059cbb000000000000000000000000d20d2f4c0b595abedef821a4157b0b990a37dae60000000000000000000000000000000000000000000000008ac7230489e80000\",\"to\":\"0x59a524d1f5dcbde3224fd42171795283596a8103\",\"value\":\"0x0\"},\"result\":{\"gasUsed\":\"0x3c36\",\"output\":\"0x0000000000000000000000000000000000000000000000000000000000000001\"},\"subtraces\":0,\"traceAddress\":[],\"type\":\"call\"}],\"transactionHash\":\"0x17dc0fef36bb997c79ee2a0a126d059227000a2d47c9bbd1f49b5902a4e7385a\",\"vmTrace\":null}, (...)]")] - ResultWrapper> trace_replayBlockTransactions([JsonRpcParameter(ExampleValue = "[\"0x88df42\",[\"trace\"]]")] BlockParameter numberOrTag, string[] traceTypes); + ResultWrapper> trace_replayBlockTransactions([JsonRpcParameter(ExampleValue = "[\"0x88df42\",[\"trace\"]]")] BlockParameter blockParameter, string[] traceTypes); [JsonRpcMethod(Description = "", IsImplemented = true, IsSharable = false)] ResultWrapper> trace_filter(TraceFilterForRpc traceFilterForRpc); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ParityTxTraceFromReplay.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ParityTxTraceFromReplay.cs index 5d139219586..efe90ca8b37 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ParityTxTraceFromReplay.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ParityTxTraceFromReplay.cs @@ -33,17 +33,16 @@ public ParityTxTraceFromReplay(IReadOnlyCollection txTraces, StateChanges = txTrace.StateChanges; TransactionHash = includeTransactionHash ? txTrace.TransactionHash : null; } - } - public byte[] Output { get; set; } + public byte[]? Output { get; set; } - public Keccak TransactionHash { get; set; } + public Keccak? TransactionHash { get; set; } - public ParityVmTrace VmTrace { get; set; } + public ParityVmTrace? VmTrace { get; set; } - public ParityTraceAction Action { get; set; } + public ParityTraceAction? Action { get; set; } - public Dictionary StateChanges { get; set; } + public Dictionary? StateChanges { get; set; } } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ParityTxTraceFromStore.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ParityTxTraceFromStore.cs index c85353106fc..bcdd827a40d 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ParityTxTraceFromStore.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/ParityTxTraceFromStore.cs @@ -11,45 +11,50 @@ namespace Nethermind.JsonRpc.Modules.Trace { public class ParityTxTraceFromStore { - public static ParityTxTraceFromStore[] FromTxTrace(ParityLikeTxTrace txTrace) + public static IEnumerable FromTxTrace(ParityLikeTxTrace txTrace) { - List results = new(); - AddActionsRecursively(results, txTrace, txTrace.Action); - return results.ToArray(); + return ReturnActionsRecursively(txTrace, txTrace.Action); } public static IEnumerable FromTxTrace(IReadOnlyCollection txTrace) { - List results = new(); foreach (ParityLikeTxTrace tx in txTrace) { - AddActionsRecursively(results, tx, tx.Action); + foreach (ParityTxTraceFromStore trace in ReturnActionsRecursively(tx, tx.Action)) + { + yield return trace; + } } - - return results; - } - private static void AddActionsRecursively(List results, ParityLikeTxTrace txTrace, ParityTraceAction txTraceAction) + private static IEnumerable ReturnActionsRecursively(ParityLikeTxTrace txTrace, ParityTraceAction? txTraceAction) { - ParityTxTraceFromStore result = new() - { - Action = txTraceAction, - Result = txTraceAction.Result, - Subtraces = txTraceAction.Subtraces.Count, - Type = txTraceAction.Type, - BlockHash = txTrace.BlockHash, - BlockNumber = txTrace.BlockNumber, - TransactionHash = txTrace.TransactionHash, - TransactionPosition = txTrace.TransactionPosition, - TraceAddress = txTraceAction.TraceAddress, - Error = txTraceAction.Error - }; - results.Add(result); - - foreach (ParityTraceAction subtrace in txTraceAction.Subtraces) + if (txTraceAction is not null) { - AddActionsRecursively(results, txTrace, subtrace); + ParityTxTraceFromStore result = new() + { + Action = txTraceAction, + Result = txTraceAction.Result, + Subtraces = txTraceAction.Subtraces.Count, + Type = txTraceAction.Type, + BlockHash = txTrace.BlockHash, + BlockNumber = txTrace.BlockNumber, + TransactionHash = txTrace.TransactionHash, + TransactionPosition = txTrace.TransactionPosition, + TraceAddress = txTraceAction.TraceAddress, + Error = txTraceAction.Error + }; + yield return result; + + for (int index = 0; index < txTraceAction.Subtraces.Count; index++) + { + ParityTraceAction subtrace = txTraceAction.Subtraces[index]; + foreach (ParityTxTraceFromStore convertedSubtrace in ReturnActionsRecursively(txTrace, subtrace)) + { + yield return convertedSubtrace; + } + + } } } @@ -64,7 +69,6 @@ private ParityTxTraceFromStore() [JsonConverter(typeof(LongConverter), NumberConversion.Raw)] public long BlockNumber { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Include)] public ParityTraceResult Result { get; set; } public int Subtraces { get; set; } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceModuleFactory.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceModuleFactory.cs index d520ee00632..dea3da6e399 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceModuleFactory.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceModuleFactory.cs @@ -31,7 +31,6 @@ public class TraceModuleFactory : ModuleFactoryBase private readonly ILogManager _logManager; private readonly IBlockPreprocessorStep _recoveryStep; private readonly IRewardCalculatorSource _rewardCalculatorSource; - private ILogger _logger; public TraceModuleFactory( IDbProvider dbProvider, @@ -49,12 +48,11 @@ public TraceModuleFactory( _trieNodeResolver = trieNodeResolver; _jsonRpcConfig = jsonRpcConfig ?? throw new ArgumentNullException(nameof(jsonRpcConfig)); _recoveryStep = recoveryStep ?? throw new ArgumentNullException(nameof(recoveryStep)); - _rewardCalculatorSource = - rewardCalculatorSource ?? throw new ArgumentNullException(nameof(rewardCalculatorSource)); + _rewardCalculatorSource = rewardCalculatorSource ?? throw new ArgumentNullException(nameof(rewardCalculatorSource)); _receiptStorage = receiptFinder ?? throw new ArgumentNullException(nameof(receiptFinder)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - _logger = logManager.GetClassLogger(); + logManager.GetClassLogger(); } public override ITraceRpcModule Create() diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs index c676c6d9fb4..746acac83d2 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs @@ -46,10 +46,8 @@ public TraceRpcModule(IReceiptFinder? receiptFinder, ITracer? tracer, IBlockFind _cancellationTokenTimeout = TimeSpan.FromMilliseconds(_jsonRpcConfig.Timeout); } - private static ParityTraceTypes GetParityTypes(string[] types) - { - return types.Select(s => FastEnum.Parse(s, true)).Aggregate((t1, t2) => t1 | t2); - } + public static ParityTraceTypes GetParityTypes(string[] types) => + types.Select(s => FastEnum.Parse(s, true)).Aggregate((t1, t2) => t1 | t2); public ResultWrapper trace_call(TransactionForRpc call, string[] traceTypes, BlockParameter? blockParameter = null) { @@ -216,6 +214,12 @@ public ResultWrapper> trace_block(BlockParam public ResultWrapper> trace_get(Keccak txHash, long[] positions) { ResultWrapper> traceTransaction = trace_transaction(txHash); + List traces = ExtractPositionsFromTxTrace(positions, traceTransaction); + return ResultWrapper>.Success(traces); + } + + public static List ExtractPositionsFromTxTrace(long[] positions, ResultWrapper> traceTransaction) + { List traces = new(); ParityTxTraceFromStore[] transactionTraces = traceTransaction.Data.ToArray(); for (int index = 0; index < positions.Length; index++) @@ -228,7 +232,7 @@ public ResultWrapper> trace_get(Keccak txHas } } - return ResultWrapper>.Success(traces); + return traces; } public ResultWrapper> trace_transaction(Keccak txHash) diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Eth62ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Eth62ProtocolHandler.cs index bc30668fc8f..5ccbd06719f 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Eth62ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Eth62ProtocolHandler.cs @@ -120,14 +120,11 @@ bool CanAcceptBlockGossip() int packetType = message.PacketType; if (!_statusReceived && packetType != Eth62MessageCode.Status) { - throw new SubprotocolException( - $"No {nameof(StatusMessage)} received prior to communication with {Node:c}."); + throw new SubprotocolException($"No {nameof(StatusMessage)} received prior to communication with {Node:c}."); } int size = message.Content.ReadableBytes; - if (Logger.IsTrace) - Logger.Trace( - $"{Counter:D5} {Eth62MessageCode.GetDescription(packetType)} from {Node:c}"); + if (Logger.IsTrace) Logger.Trace($"{Counter:D5} {Eth62MessageCode.GetDescription(packetType)} from {Node:c}"); switch (packetType) { diff --git a/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj b/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj index 2d96158ac92..a6a6e27dacb 100644 --- a/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj +++ b/src/Nethermind/Nethermind.Runner/Nethermind.Runner.csproj @@ -44,6 +44,7 @@ + @@ -107,8 +108,8 @@ - - + + diff --git a/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs b/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs index 7dd04d2696a..6ab7b5ecd2f 100644 --- a/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs +++ b/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text; +using Nethermind.Core.Collections; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -14,15 +15,19 @@ namespace Nethermind.Serialization.Json { public class EthereumJsonSerializer : IJsonSerializer { + private readonly int? _maxDepth; private JsonSerializer _internalSerializer; private JsonSerializer _internalReadableSerializer; private JsonSerializerSettings _settings; private JsonSerializerSettings _readableSettings; - public EthereumJsonSerializer() + public EthereumJsonSerializer(int? maxDepth = null, params JsonConverter[] converters) { - RebuildSerializers(); + _maxDepth = maxDepth; + BasicConverters.AddRange(converters); + ReadableConverters.AddRange(converters); + RebuildSerializers(maxDepth); } public static IReadOnlyList CommonConverters { get; } = new ReadOnlyCollection( @@ -125,17 +130,17 @@ public void RegisterConverter(JsonConverter converter) BasicConverters.Add(converter); ReadableConverters.Add(converter); - RebuildSerializers(); + RebuildSerializers(_maxDepth); } - private void RebuildSerializers() + private void RebuildSerializers(int? maxDepth = null) { _readableSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented, - Converters = ReadableConverters + Converters = ReadableConverters, }; _settings = new JsonSerializerSettings @@ -146,6 +151,11 @@ private void RebuildSerializers() Converters = BasicConverters, }; + if (maxDepth is not null) + { + _readableSettings.MaxDepth = _settings.MaxDepth = maxDepth.Value; + } + _internalSerializer = JsonSerializer.Create(_settings); _internalReadableSerializer = JsonSerializer.Create(_readableSettings); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/RlpDecoderExtensions.cs b/src/Nethermind/Nethermind.Serialization.Rlp/RlpDecoderExtensions.cs index 66022ff453c..e0188ced7b5 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/RlpDecoderExtensions.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/RlpDecoderExtensions.cs @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Collections.Generic; + namespace Nethermind.Serialization.Rlp { public static class RlpDecoderExtensions @@ -44,5 +46,23 @@ public static Rlp Encode(this IRlpObjectDecoder decoder, T?[]? items, RlpB return Rlp.Encode(rlpSequence); } + + public static Rlp Encode(this IRlpObjectDecoder decoder, IReadOnlyCollection? items, RlpBehaviors behaviors = RlpBehaviors.None) + { + if (items == null) + { + return Rlp.OfEmptySequence; + } + + Rlp[] rlpSequence = new Rlp[items.Count]; + int i = 0; + foreach (T? item in items) + { + rlpSequence[i++] = item == null ? Rlp.OfEmptySequence : decoder.Encode(item, behaviors); + } + + return Rlp.Encode(rlpSequence); + } + } } diff --git a/src/Nethermind/Nethermind.State/IStateTracer.cs b/src/Nethermind/Nethermind.State/IStateTracer.cs index eb778c951c4..1d62d72ab72 100644 --- a/src/Nethermind/Nethermind.State/IStateTracer.cs +++ b/src/Nethermind/Nethermind.State/IStateTracer.cs @@ -8,10 +8,50 @@ namespace Nethermind.State { public interface IStateTracer { + /// + /// + /// + /// + /// Controls + /// - + /// - + /// - + /// - + /// bool IsTracingState { get; } + + /// + /// Reports change of balance for address + /// + /// + /// + /// + /// Depends on void ReportBalanceChange(Address address, UInt256? before, UInt256? after); + + /// + /// Reports change of code for address + /// + /// + /// + /// + /// Depends on void ReportCodeChange(Address address, byte[]? before, byte[]? after); + + /// + /// Reports change of nonce for address + /// + /// + /// + /// + /// Depends on void ReportNonceChange(Address address, UInt256? before, UInt256? after); + + /// + /// Reports accessing the address + /// + /// + /// Depends on void ReportAccountRead(Address address); } } diff --git a/src/Nethermind/Nethermind.State/IStorageTracer.cs b/src/Nethermind/Nethermind.State/IStorageTracer.cs index 4f2bba40a29..1e150a9bae0 100644 --- a/src/Nethermind/Nethermind.State/IStorageTracer.cs +++ b/src/Nethermind/Nethermind.State/IStorageTracer.cs @@ -1,14 +1,45 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Nethermind.Core; namespace Nethermind.State { public interface IStorageTracer { + /// + /// Controls tracing of storage + /// + /// + /// Controls + /// - + /// - + /// bool IsTracingStorage { get; } + + /// + /// Reports change of storage slot for key + /// + /// + /// + /// Depends on + void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value); + + /// + /// Reports change of storage slot for key + /// + /// + /// + /// + /// Depends on void ReportStorageChange(StorageCell storageCell, byte[] before, byte[] after); + + /// + /// Reports storage access + /// + /// + /// Depends on void ReportStorageRead(StorageCell storageCell); } } diff --git a/src/Nethermind/Nethermind.State/NullStorageTracer.cs b/src/Nethermind/Nethermind.State/NullStorageTracer.cs index ab90ca69e2c..34af299d30d 100644 --- a/src/Nethermind/Nethermind.State/NullStorageTracer.cs +++ b/src/Nethermind/Nethermind.State/NullStorageTracer.cs @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only +// SPDX-License-Identifier: LGPL-3.0-only using System; using Nethermind.Core; @@ -15,6 +15,9 @@ private NullStorageTracer() { } private const string ErrorMessage = "Null tracer should never receive any calls."; public bool IsTracingStorage => false; + public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) + => throw new InvalidOperationException(ErrorMessage); + public void ReportStorageChange(StorageCell storageCell, byte[] before, byte[] after) => throw new InvalidOperationException(ErrorMessage); diff --git a/src/Nethermind/Nethermind.TxPool/TxBroadcaster.cs b/src/Nethermind/Nethermind.TxPool/TxBroadcaster.cs index 73131ad1ce8..359c0b6efba 100644 --- a/src/Nethermind/Nethermind.TxPool/TxBroadcaster.cs +++ b/src/Nethermind/Nethermind.TxPool/TxBroadcaster.cs @@ -175,7 +175,7 @@ public void StopBroadcast(Keccak txHash) { if (_persistentTxs.Count != 0) { - bool hasBeenRemoved = _persistentTxs.TryRemove(txHash, out Transaction _); + bool hasBeenRemoved = _persistentTxs.TryRemove(txHash, out Transaction? _); if (hasBeenRemoved) { if (_logger.IsTrace) _logger.Trace( diff --git a/src/Nethermind/Nethermind.sln b/src/Nethermind/Nethermind.sln index 3c2c54c2241..9af8806130e 100644 --- a/src/Nethermind/Nethermind.sln +++ b/src/Nethermind/Nethermind.sln @@ -228,6 +228,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Merge.AuRa.Test" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Merge.AuRa", "Nethermind.Merge.AuRa\Nethermind.Merge.AuRa.csproj", "{F166365D-6E31-401B-9753-59EADB800E2A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TraceStore", "TraceStore", "{06AF5D92-0E47-4A98-AF49-492F814618E3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nethermind.JsonRpc.TraceStore", "Nethermind.JsonRpc.TraceStore\Nethermind.JsonRpc.TraceStore.csproj", "{D4C5A63E-8E47-43D3-B726-B1D95591100C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nethermind.JsonRpc.TraceStore.Tests", "Nethermind.JsonRpc.TraceStore.Tests\Nethermind.JsonRpc.TraceStore.Tests.csproj", "{B4A10FCA-4982-4D89-8D8C-6154882938E4}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UPnP", "UPnP", "{2EDE2554-59C0-4E78-92F7-EB8077AA597D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nethermind.UPnP.Plugin", "Nethermind.UPnP.Plugin\Nethermind.UPnP.Plugin.csproj", "{48E50409-26FE-4FD8-AF6E-2A0E79F794CE}" @@ -598,10 +604,18 @@ Global {F166365D-6E31-401B-9753-59EADB800E2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {F166365D-6E31-401B-9753-59EADB800E2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F166365D-6E31-401B-9753-59EADB800E2A}.Release|Any CPU.Build.0 = Release|Any CPU + {D4C5A63E-8E47-43D3-B726-B1D95591100C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4C5A63E-8E47-43D3-B726-B1D95591100C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4C5A63E-8E47-43D3-B726-B1D95591100C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4C5A63E-8E47-43D3-B726-B1D95591100C}.Release|Any CPU.Build.0 = Release|Any CPU + {B4A10FCA-4982-4D89-8D8C-6154882938E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4A10FCA-4982-4D89-8D8C-6154882938E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4A10FCA-4982-4D89-8D8C-6154882938E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4A10FCA-4982-4D89-8D8C-6154882938E4}.Release|Any CPU.Build.0 = Release|Any CPU {48E50409-26FE-4FD8-AF6E-2A0E79F794CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {48E50409-26FE-4FD8-AF6E-2A0E79F794CE}.Debug|Any CPU.Build.0 = Debug|Any CPU {48E50409-26FE-4FD8-AF6E-2A0E79F794CE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48E50409-26FE-4FD8-AF6E-2A0E79F794CE}.Release|Any CPU.Build.0 = Release|Any CPU + {48E50409-26FE-4FD8-AF6E-2A0E79F794CE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -660,8 +674,11 @@ Global {1BA992CF-157C-4E0C-B031-46502D1AFD7D} = {4019B82F-1104-4D2C-9F96-05FD7D3575E8} {25910851-D58B-42D5-83F1-0BA530CD8E1E} = {4019B82F-1104-4D2C-9F96-05FD7D3575E8} {0DD96857-0F06-42CA-B249-45B4AF4E81CA} = {4019B82F-1104-4D2C-9F96-05FD7D3575E8} + {06AF5D92-0E47-4A98-AF49-492F814618E3} = {78BED57D-720E-4E6C-ABA2-397B73B494F9} + {D4C5A63E-8E47-43D3-B726-B1D95591100C} = {06AF5D92-0E47-4A98-AF49-492F814618E3} + {B4A10FCA-4982-4D89-8D8C-6154882938E4} = {06AF5D92-0E47-4A98-AF49-492F814618E3} {2EDE2554-59C0-4E78-92F7-EB8077AA597D} = {78BED57D-720E-4E6C-ABA2-397B73B494F9} - {48E50409-26FE-4FD8-AF6E-2A0E79F794CE} = {2EDE2554-59C0-4E78-92F7-EB8077AA597D} + {48E50409-26FE-4FD8-AF6E-2A0E79F794CE} = {2EDE2554-59C0-4E78-92F7-EB8077AA597D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {092CA5E3-6180-4ED7-A3CB-9B57FAC2AA85}