Skip to content

Commit

Permalink
Feat/native 4byte tracer (#6864)
Browse files Browse the repository at this point in the history
Co-authored-by: lukasz.rozmej <[email protected]>
  • Loading branch information
natebeauregard and LukaszRozmej authored Mar 28, 2024
1 parent 879d52f commit 55a6ec2
Show file tree
Hide file tree
Showing 26 changed files with 439 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
using Nethermind.Consensus.Validators;
using Nethermind.Consensus.Withdrawals;
using Nethermind.Core.Specs;
using Nethermind.Db;
using Nethermind.Evm.Tracing.GethStyle.JavaScript;
using Nethermind.Logging;
using Nethermind.State;
using Nethermind.TxPool;
Expand Down
12 changes: 8 additions & 4 deletions src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
using Nethermind.Crypto;
using Nethermind.Evm.Tracing;
using Nethermind.Evm.Tracing.GethStyle;
using Nethermind.Evm.Tracing.GethStyle.JavaScript;
using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;
using Nethermind.Evm.Tracing.GethStyle.Custom.Native;
using Nethermind.Evm.TransactionProcessing;
using Nethermind.Serialization.Rlp;
using Nethermind.State;
Expand Down Expand Up @@ -169,9 +170,12 @@ public IEnumerable<string> TraceBlockToFile(Hash256 blockHash, GethTraceOptions
}

private IBlockTracer<GethLikeTxTrace> CreateOptionsTracer(BlockHeader block, GethTraceOptions options) =>
!string.IsNullOrEmpty(options.Tracer)
? new GethLikeBlockJavaScriptTracer(_worldState, _specProvider.GetSpec(block), options)
: new GethLikeBlockMemoryTracer(options);
options switch
{
{ Tracer: var t } when GethLikeNativeTracerFactory.IsNativeTracer(t) => new GethLikeBlockNativeTracer(options),
{ Tracer.Length: > 0 } => new GethLikeBlockJavaScriptTracer(_worldState, _specProvider.GetSpec(block), options),
_ => new GethLikeBlockMemoryTracer(options),
};

private IReadOnlyCollection<GethLikeTxTrace> TraceBlock(Block? block, GethTraceOptions options, CancellationToken cancellationToken)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Core/Extensions/Bytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ static void ThrowArgumentOutOfRangeException()
}
}

internal static void OutputBytesToCharHex(ref byte input, int length, ref char charsRef, bool withZeroX, int leadingZeros)
public static void OutputBytesToCharHex(ref byte input, int length, ref char charsRef, bool withZeroX, int leadingZeros)
{
if (withZeroX)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections;
using System.Collections.Generic;
using Nethermind.Core.Test.Builders;
using Nethermind.Evm.Precompiles;
using Nethermind.Evm.Tracing.GethStyle.Custom.Native.Tracers;
using NUnit.Framework;
namespace Nethermind.Evm.Test.Tracing;

[TestFixture]
public class GethLike4byteTracerTests : GethLikeNativeTracerTestsBase
{
[TestCaseSource(nameof(FourByteTracerTests))]
public Dictionary<string, int>? four_byte_tracer_executes_correctly(byte[] code, byte[]? input) =>
(Dictionary<string, int>)ExecuteAndTrace(Native4ByteTracer.FourByteTracer, code, input).CustomTracerResult?.Value;

private static IEnumerable FourByteTracerTests
{
get
{
byte[] sampleInput = Prepare.EvmCode
.PushData(SampleHexData2)
.STOP()
.Done;
byte[] callEvmCode = Prepare.EvmCode
.CallWithInput(TestItem.AddressA, 50000, sampleInput)
.CallWithInput(TestItem.AddressA, 50000, sampleInput)
.CallWithInput(TestItem.AddressA, 50000, new byte[6])
.STOP()
.Done;
yield return new TestCaseData(callEvmCode, null)
{
TestName = "Tracing CALL execution",
ExpectedResult = new Dictionary<string, int>
{
{ "62b15678-1", 2 },
{ "00000000-2", 1 }
}
};

byte[] delegateCallEvmCode = Prepare.EvmCode
.DelegateCall(TestItem.AddressC, 50000)
.STOP()
.Done;
var singleCall4ByteIds = new Dictionary<string, int>
{
{ "62b15678-1", 1 }
};
yield return new TestCaseData(delegateCallEvmCode, sampleInput)
{
TestName = "Tracing DELEGATECALL execution",
ExpectedResult = singleCall4ByteIds
};

byte[] staticCallEvmCode = Prepare.EvmCode
.StaticCall(TestItem.AddressC, 50000)
.STOP()
.Done;
yield return new TestCaseData(staticCallEvmCode, sampleInput)
{
TestName = "Tracing STATICCALL execution",
ExpectedResult = singleCall4ByteIds
};

byte[] callCodeEvmCode = Prepare.EvmCode
.CallCode(TestItem.AddressC, 50000)
.STOP()
.Done;
yield return new TestCaseData(callCodeEvmCode, sampleInput)
{
TestName = "Tracing CALLCODE execution",
ExpectedResult = singleCall4ByteIds
};

byte[] callEvmCodeLessThan3Bytes = Prepare.EvmCode
.CallWithInput(TestItem.AddressA, 50000, Prepare.EvmCode.STOP().Done)
.CallWithInput(TestItem.AddressA, 50000, new byte[3])
.STOP()
.Done;
yield return new TestCaseData(callEvmCodeLessThan3Bytes, null)
{
TestName = "Tracing CALL execution with input less than 4 bytes",
ExpectedResult = new Dictionary<string, int>()
};

byte[] callEvmCodePrecompile = Prepare.EvmCode
.CallWithInput(IdentityPrecompile.Address, 50000, sampleInput)
.STOP()
.Done;
yield return new TestCaseData(callEvmCodePrecompile, null)
{
TestName = "Tracing CALL execution with precompile",
ExpectedResult = new Dictionary<string, int>()
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using NUnit.Framework;
using Nethermind.Specs;
using Nethermind.Core.Test.Builders;
using Nethermind.Evm.Tracing.GethStyle.JavaScript;
using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;
using Nethermind.Int256;
using Nethermind.Serialization.Json;
using Nethermind.Specs.Forks;
Expand Down Expand Up @@ -346,6 +346,20 @@ public void call_tracer()
NestedCalls(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();

Assert.That(JsonSerializer.Serialize(traces.CustomTracerResult?.Value), Is.EqualTo("{\"type\":\"CALL\",\"from\":\"b7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"to\":\"942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"value\":\"0x1\",\"gas\":\"0x186a0\",\"gasUsed\":\"0xdbd1\",\"input\":\"\",\"output\":\"\",\"calls\":[{\"type\":\"DELEGATECALL\",\"from\":\"942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"to\":\"76e68a8696537e4141926f3e528733af9e237d69\",\"gas\":\"0xc350\",\"gasUsed\":\"0x14d07\",\"input\":\"\",\"output\":\"\",\"calls\":[{\"type\":\"CREATE\",\"from\":\"942921b14f1b1c385cd7e0cc2ef7abe5598c8358\",\"to\":\"89aa9b2ce05aaef815f25b237238c0b4ffff6ae3\",\"value\":\"0x0\",\"gas\":\"0x4513\",\"gasUsed\":\"0x7f6e\",\"input\":\"7f000000000000000000000000000000000000000000000000000000000000000060005260036000f3\",\"output\":\"000000\"}]}]}"));
}

[Test]
public void _4byte_tracer_legacy()
{
GethLikeTxTrace traces = ExecuteBlock(
GetTracer("4byteTracer_legacy"),
CallWithInput(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();

Assert.That(JsonSerializer.Serialize(traces.CustomTracerResult?.Value), Is.EqualTo("{\"00000000-1\":2,\"00000000-2\":1}"));
}

[Test]
Expand Down Expand Up @@ -421,6 +435,18 @@ private byte[] NestedCalls()
.Done;
}

private byte[] CallWithInput()
{
byte[] input = new byte[5];
byte[] input2 = new byte[6];

return Prepare.EvmCode
.CallWithInput(TestItem.AddressC, 50000, input)
.CallWithInput(TestItem.AddressC, 50000, input)
.CallWithInput(TestItem.AddressC, 50000, input2)
.Done;
}

[Test]
public void complex_tracer()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using Nethermind.Evm.Tracing.GethStyle;
using Nethermind.Evm.Tracing.GethStyle.Custom.Native;
using Nethermind.Evm.Tracing.GethStyle.Custom.Native.Tracers;
using NUnit.Framework;
namespace Nethermind.Evm.Test.Tracing;

public class GethLikeNativeTracerFactoryTests
{
[Test]
public void CreateTracer_NativeTracerExists()
{
var options = new GethTraceOptions { Tracer = Native4ByteTracer.FourByteTracer };

GethLikeNativeTxTracer? nativeTracer = GethLikeNativeTracerFactory.CreateTracer(options);

Assert.True(nativeTracer is Native4ByteTracer);
}

[Test]
public void CreateTracer_NativeTracerDoesNotExist()
{
var options = new GethTraceOptions { Tracer = "nonExistentTracer" };

Assert.Throws<ArgumentException>(() => GethLikeNativeTracerFactory.CreateTracer(options));
}

[Test]
public void IsNativeTracer_TracerNameExists()
{
var isNativeTracer = GethLikeNativeTracerFactory.IsNativeTracer(Native4ByteTracer.FourByteTracer);

Assert.True(isNativeTracer);
}

[Test]
public void IsNativeTracer_TracerNameDoesNotExist()
{
var isNativeTracer = GethLikeNativeTracerFactory.IsNativeTracer("nonExistentTracer");

Assert.False(isNativeTracer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;
using Nethermind.Evm.Tracing.GethStyle;
using Nethermind.Evm.Tracing.GethStyle.Custom.Native;
using Nethermind.Int256;

namespace Nethermind.Evm.Test.Tracing;

public class GethLikeNativeTracerTestsBase : VirtualMachineTestsBase
{
protected GethLikeTxTrace ExecuteAndTrace(string tracerName, byte[] code, byte[]? input = default, UInt256 value = default)
{
GethLikeNativeTxTracer tracer = GethLikeNativeTracerFactory.CreateTracer(GethTraceOptions.Default with { Tracer = tracerName });
(Block block, Transaction transaction) = input is null ? PrepareTx(Activation, 100000, code) : PrepareTx(Activation, 100000, code, input, value);
_processor.Execute(transaction, block.Header, tracer);
return tracer.BuildResult();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// 4byteTracer searches for 4byte-identifiers, and collects them for post-processing.
// 4byteTracer_legacy searches for 4byte-identifiers, and collects them for post-processing.
// It collects the methods identifiers along with the size of the supplied data, so
// a reversed signature can be matched against the size of the data.
//
// Example:
// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer_legacy"})
// {
// 0x27dc297e-128: 1,
// 0x38cc4831-0: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@

using System.Text.Json.Serialization;

using Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom;

namespace Nethermind.Evm.Tracing.GethStyle;

[JsonConverter(typeof(GethLikeJavaScriptTraceConverter))]
public class GethLikeJavaScriptTrace
[JsonConverter(typeof(GethLikeCustomTraceConverter))]
public class GethLikeCustomTrace
{
private static readonly object _empty = new { };
public object Value { get; set; } = _empty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

using Nethermind.Serialization.Json;

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom;

public class GethLikeJavaScriptTraceConverter : JsonConverter<GethLikeJavaScriptTrace>
public class GethLikeCustomTraceConverter : JsonConverter<GethLikeCustomTrace>
{
public override void Write(Utf8JsonWriter writer, GethLikeJavaScriptTrace value, JsonSerializerOptions options)
public override void Write(Utf8JsonWriter writer, GethLikeCustomTrace value, JsonSerializerOptions options)
{
if (value is null)
{
Expand All @@ -31,7 +31,7 @@ public override void Write(Utf8JsonWriter writer, GethLikeJavaScriptTrace value,
}
}

public override GethLikeJavaScriptTrace? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
public override GethLikeCustomTrace? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using Nethermind.Core;
using Nethermind.Int256;

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;

public record CallFrame
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using Nethermind.Core.Crypto;
using Nethermind.Int256;

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;

public class Context
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
using Nethermind.Int256;
using Nethermind.State;

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;

public class Db
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
using Nethermind.Logging;
#pragma warning disable CS0162 // Unreachable code detected

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;

public class Engine : IDisposable
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using Microsoft.ClearScript.JavaScript;
using Microsoft.ClearScript.V8;

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;

public class FrameResult
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using Nethermind.Int256;
using Nethermind.State;

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;

public class GethLikeBlockJavaScriptTracer : BlockTracerBase<GethLikeTxTrace, GethLikeJavaScriptTxTracer>, IDisposable
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using Nethermind.Int256;
using Nethermind.Core.Crypto;

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;

public sealed class GethLikeJavaScriptTxTracer : GethLikeTxTracer, ITxTracer
{
Expand Down Expand Up @@ -59,7 +59,7 @@ public GethLikeJavaScriptTxTracer(
public override GethLikeTxTrace BuildResult()
{
GethLikeTxTrace result = base.BuildResult();
result.CustomTracerResult = new GethLikeJavaScriptTrace() { Value = _tracer.result(_ctx, _db) };
result.CustomTracerResult = new GethLikeCustomTrace() { Value = _tracer.result(_ctx, _db) };
_resultConstructed = true;
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using Nethermind.Core.Extensions;
using Nethermind.Int256;

namespace Nethermind.Evm.Tracing.GethStyle.JavaScript;
namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript;

public static class JavaScriptConverter
{
Expand Down
Loading

0 comments on commit 55a6ec2

Please sign in to comment.