Skip to content

Commit

Permalink
Fix TraceStore plugin (#6609)
Browse files Browse the repository at this point in the history
Co-authored-by: Ben Adams <thundercat@illyriad.co.uk>
  • Loading branch information
LukaszRozmej and benaadams authored Jan 25, 2024
1 parent 405591e commit ecd8a2a
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@

namespace Nethermind.Evm.Tracing.ParityStyle
{
[JsonConverter(typeof(ParityTraceActionConverter))]
public class ParityTraceAction
{
public int[]? TraceAddress { get; set; }
public string? CallType { get; set; }

public bool IncludeInTrace { get; set; } = true;
public bool IsPrecompiled { get; set; }
public string? Type { get; set; }
Expand All @@ -26,7 +24,6 @@ public class ParityTraceAction
public byte[]? Input { get; set; }
public ParityTraceResult? Result { get; set; } = new();
public List<ParityTraceAction> Subtraces { get; set; } = new();

public Address? Author { get; set; }
public string? RewardType { get; set; }
public string? Error { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace Nethermind.Evm.Tracing.ParityStyle
*/
public class ParityTraceActionConverter : JsonConverter<ParityTraceAction>
{
public static readonly ParityTraceActionConverter Instance = new();

public override ParityTraceAction Read(
ref Utf8JsonReader reader,
Type typeToConvert,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

using Nethermind.JsonRpc.Data;
using Nethermind.JsonRpc.Modules.Eth;
using Nethermind.JsonRpc.Modules.Trace;
using Nethermind.Serialization.Json;

using NUnit.Framework;
Expand Down Expand Up @@ -47,32 +44,23 @@ protected void TestRoundtrip<T>(T item, Func<T, T, bool>? equalityComparer, stri
TestRoundtrip(item, equalityComparer, null, description);
}

protected void TestRoundtrip<T>(string json, JsonConverter? converter = null)
protected void TestRoundtrip<T>(string json, params JsonConverter[] converters)
{
IJsonSerializer serializer = BuildSerializer();
IJsonSerializer serializer = BuildSerializer(converters);

T deserialized = serializer.Deserialize<T>(json);
string result = serializer.Serialize(deserialized);
Assert.That(result, Is.EqualTo(json));
}

private void TestToJson<T>(T item, JsonConverter<T>? converter, string expectedResult)
protected void TestToJson<T>(T item, string expectedResult, params JsonConverter[] converters)
{
IJsonSerializer serializer = BuildSerializer();
IJsonSerializer serializer = BuildSerializer(converters);

string result = serializer.Serialize(item);
Assert.That(result, Is.EqualTo(expectedResult.Replace("+", "\\u002B")), result.Replace("\"", "\\\""));
}

protected void TestToJson<T>(T item, string expectedResult)
{
TestToJson(item, null, expectedResult);
}

private static IJsonSerializer BuildSerializer()
{
IJsonSerializer serializer = new EthereumJsonSerializer();
return serializer;
}
private static IJsonSerializer BuildSerializer(params JsonConverter[] converters) => new EthereumJsonSerializer(converters);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,26 @@ public class ParityTraceActionSerializationTests : SerializationTestBase
[Test]
public void Can_serialize()
{
ParityTraceAction action = new();
action.From = TestItem.AddressA;
action.Gas = 12345;
action.Input = new byte[] { 6, 7, 8, 9, 0 };
action.To = TestItem.AddressB;
action.Value = 24680;
action.CallType = "call";
action.TraceAddress = new int[] { 1, 3, 5, 7 };
ParityTraceAction action = new()
{
From = TestItem.AddressA,
Gas = 12345,
Input = [6, 7, 8, 9, 0],
To = TestItem.AddressB,
Value = 24680,
CallType = "call",
TraceAddress = [1, 3, 5, 7]
};

TestToJson(action, "{\"callType\":\"call\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"gas\":\"0x3039\",\"input\":\"0x0607080900\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x6068\"}");
TestToJson(action, "{\"callType\":\"call\",\"from\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"gas\":\"0x3039\",\"input\":\"0x0607080900\",\"to\":\"0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x6068\"}", ParityTraceActionConverter.Instance);
}

[Test]
public void Can_serialize_nulls()
{
ParityTraceAction action = new();

TestToJson(action, "{\"callType\":null,\"from\":null,\"gas\":\"0x0\",\"input\":null,\"to\":null,\"value\":\"0x0\"}");
TestToJson(action, "{\"callType\":null,\"from\":null,\"gas\":\"0x0\",\"input\":null,\"to\":null,\"value\":\"0x0\"}", ParityTraceActionConverter.Instance);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Test.Builders;
using Nethermind.Db;
using Nethermind.Evm;
using Nethermind.Evm.Tracing;
using Nethermind.Evm.Tracing.ParityStyle;
using Nethermind.Logging;
using NUnit.Framework;
Expand All @@ -15,24 +20,117 @@ namespace Nethermind.JsonRpc.TraceStore.Tests;
[Parallelizable(ParallelScope.All)]
public class DbPersistingBlockTracerTests
{
private class Test
{
public DbPersistingBlockTracer<ParityLikeTxTrace, ParityLikeTxTracer> DbPersistingTracer { get; }
public ParityLikeTraceSerializer Serializer { get; }
public MemDb Db { get; }

public Test()
{
ParityLikeBlockTracer parityTracer = new(ParityTraceTypes.Trace);
Db = new();
Serializer = new(LimboLogs.Instance);
DbPersistingTracer = new(parityTracer, Db, Serializer, LimboLogs.Instance);
}

public (Hash256 hash, List<ParityLikeTxTrace> traces) Trace(Action<ITxTracer>? customTrace = null)
{
Transaction transaction = Build.A.Transaction.TestObject;
Block block = Build.A.Block.WithTransactions(transaction).TestObject;
DbPersistingTracer.StartNewBlockTrace(block);
ITxTracer txTracer = DbPersistingTracer.StartNewTxTrace(transaction);
customTrace?.Invoke(txTracer);
DbPersistingTracer.EndTxTrace();
DbPersistingTracer.EndBlockTrace();
Hash256 hash = block.Hash!;
return (hash, Serializer.Deserialize(Db.Get(hash))!);
}
}

[Test]
public void saves_traces_to_db()
{
ParityLikeBlockTracer parityTracer = new(ParityTraceTypes.Trace);
MemDb memDb = new();
ParityLikeTraceSerializer serializer = new(LimboLogs.Instance);
DbPersistingBlockTracer<ParityLikeTxTrace, ParityLikeTxTracer> 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<ParityLikeTxTrace>? traces = serializer.Deserialize(memDb.Get(block.Hash!));
traces.Should().BeEquivalentTo(new ParityLikeTxTrace[] { new() { BlockHash = block.Hash, TransactionPosition = 0 } });
Test test = new();
(Hash256 hash, List<ParityLikeTxTrace> traces) = test.Trace(tracer =>
{
tracer.ReportAction(100, 50, TestItem.AddressA, TestItem.AddressB, TestItem.RandomDataA, ExecutionType.CALL);
tracer.ReportAction(80, 20, TestItem.AddressB, TestItem.AddressC, TestItem.RandomDataC, ExecutionType.CREATE);
tracer.ReportActionEnd(60, TestItem.RandomDataD);
tracer.ReportActionEnd(50, TestItem.RandomDataB);
}
);

traces.Should().BeEquivalentTo(new ParityLikeTxTrace[]
{
new()
{
BlockHash = hash,
TransactionPosition = 0,
Action = new ParityTraceAction
{
CallType = "call",
From = TestItem.AddressA,
Gas = 100,
IncludeInTrace = true,
Input = TestItem.RandomDataA,
Result = new ParityTraceResult { GasUsed = 50, Output = TestItem.RandomDataB },
To = TestItem.AddressB,
TraceAddress = Array.Empty<int>(),
Type = "call",
Value = 50,
Subtraces =
[
new()
{
CallType = "create",
From = TestItem.AddressB,
Gas = 80,
IncludeInTrace = true,
Input = TestItem.RandomDataC,
Result = new ParityTraceResult { GasUsed = 20, Output = TestItem.RandomDataD },
Subtraces = new List<ParityTraceAction>(),
To = TestItem.AddressC,
TraceAddress = [0],
CreationMethod = "create",
Type = "create",
Value = 20
}
]
}
}
});
}

[TestCase(510)]
[TestCase(1020)]
[TestCase(1500)]
public void check_depth(int depth)
{
// depth = depth / 2 - 1;
Test test = new();
(_, List<ParityLikeTxTrace> traces) = test.Trace(tracer =>
{
for (int i = 0; i < depth; i++)
{
tracer.ReportAction(100, 50, TestItem.AddressA, TestItem.AddressB, TestItem.RandomDataA, ExecutionType.CALL);
}

for (int i = 0; i < depth; i++)
{
tracer.ReportActionEnd(60, TestItem.RandomDataD);
}
}
);

ParityTraceAction? action = traces.FirstOrDefault()?.Action;
int checkedDepth = 0;
while (action is not null)
{
checkedDepth += 1;
action = action.Subtraces.FirstOrDefault();
}

checkedDepth.Should().Be(depth);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface ITraceStoreConfig : IConfig
[ConfigItem(Description = "Whether to verify all serialized elements.", DefaultValue = "false", HiddenFromDocs = true)]
bool VerifySerialized { get; set; }

[ConfigItem(Description = "The max depth allowed when deserializing traces.", DefaultValue = "1024", HiddenFromDocs = true)]
[ConfigItem(Description = "The max depth allowed when deserializing traces.", DefaultValue = "3200", HiddenFromDocs = true)]
int MaxDepth { get; set; }

[ConfigItem(Description = "The max parallelization when deserialization requests the `trace_filter` method. `0` to use the number of logical processors. If you experience a resource shortage, set to a low number.", DefaultValue = "0")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Nethermind.JsonRpc.TraceStore;

public class ParityLikeTraceSerializer : ITraceSerializer<ParityLikeTxTrace>
{
public const int DefaultDepth = 3200;
private static readonly byte[] _emptyBytes = { 0 };
private static readonly List<ParityLikeTxTrace> _emptyTraces = new();

Expand All @@ -20,7 +21,7 @@ public class ParityLikeTraceSerializer : ITraceSerializer<ParityLikeTxTrace>
private readonly int _maxDepth;
private readonly bool _verifySerialized;

public ParityLikeTraceSerializer(ILogManager logManager, int maxDepth = 1024, bool verifySerialized = false)
public ParityLikeTraceSerializer(ILogManager logManager, int maxDepth = DefaultDepth, bool verifySerialized = false)
{
_jsonSerializer = new EthereumJsonSerializer(maxDepth);
_maxDepth = maxDepth;
Expand All @@ -30,7 +31,7 @@ public ParityLikeTraceSerializer(ILogManager logManager, int maxDepth = 1024, bo

public unsafe List<ParityLikeTxTrace>? Deserialize(Span<byte> serialized)
{
if (serialized.Length == 1) return _emptyTraces;
if (serialized.Length <= 1) return _emptyTraces;

fixed (byte* pBuffer = &serialized[0])
{
Expand Down Expand Up @@ -102,9 +103,9 @@ private void CheckDepth(ParityTraceAction action, int depth)
throw new ArgumentException("Trace depth is too high");
}

foreach (ParityTraceAction subAction in action.Subtraces)
for (var index = 0; index < action.Subtraces.Count; index++)
{
CheckDepth(subAction, depth);
CheckDepth(action.Subtraces[index], depth);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Evm.Tracing.ParityStyle;
using Nethermind.Serialization.Json;
using Newtonsoft.Json;

namespace Nethermind.JsonRpc.TraceStore;

Expand All @@ -11,6 +13,6 @@ public class TraceStoreConfig : ITraceStoreConfig
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;
public int MaxDepth { get; set; } = ParityLikeTraceSerializer.DefaultDepth;
public int DeserializationParallelization { get; set; } = 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public override void Write(
writer.WriteStartObject();

writer.WritePropertyName("action"u8);
JsonSerializer.Serialize(writer, value, options);
ParityTraceActionConverter.Instance.Write(writer, value, options);

if (value.Error is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ private ParityTxTraceFromStore()
{
}

[JsonConverter(typeof(ParityTraceActionConverter))]
public ParityTraceAction Action { get; set; }

public Hash256 BlockHash { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ public ResultWrapper<IEnumerable<ParityTxTraceFromStore>> trace_filter(TraceFilt
return GetStateFailureResult<IEnumerable<ParityTxTraceFromStore>>(block.Header);
}

IReadOnlyCollection<ParityLikeTxTrace> txTracesFromOneBlock = ExecuteBlock(block!, new((ParityTraceTypes)(ParityTraceTypes.Trace | ParityTraceTypes.Rewards)));
IReadOnlyCollection<ParityLikeTxTrace> txTracesFromOneBlock = ExecuteBlock(block!, new(ParityTraceTypes.Trace | ParityTraceTypes.Rewards));
txTraces.AddRange(txTracesFromOneBlock);
}

Expand All @@ -263,7 +263,7 @@ public ResultWrapper<IEnumerable<ParityTxTraceFromStore>> trace_block(BlockParam
return GetStateFailureResult<IEnumerable<ParityTxTraceFromStore>>(block.Header);
}

IReadOnlyCollection<ParityLikeTxTrace> txTraces = ExecuteBlock(block, new((ParityTraceTypes)(ParityTraceTypes.Trace | ParityTraceTypes.Rewards)));
IReadOnlyCollection<ParityLikeTxTrace> txTraces = ExecuteBlock(block, new(ParityTraceTypes.Trace | ParityTraceTypes.Rewards));
return ResultWrapper<IEnumerable<ParityTxTraceFromStore>>.Success(txTraces.SelectMany(ParityTxTraceFromStore.FromTxTrace));
}

Expand Down
Loading

0 comments on commit ecd8a2a

Please sign in to comment.