diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/AlwaysCancelTxTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/AlwaysCancelTxTracer.cs index 6d61ad0d7a46..b20980896390 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/AlwaysCancelTxTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/AlwaysCancelTxTracer.cs @@ -37,6 +37,7 @@ public static AlwaysCancelTxTracer Instance public bool IsTracingMemory => true; public bool IsTracingInstructions => true; public bool IsTracingRefunds => true; + public bool IsTracingReturnData => true; public bool IsTracingCode => true; public bool IsTracingStack => true; public bool IsTracingState => true; @@ -59,6 +60,7 @@ public static AlwaysCancelTxTracer Instance public void ReportLog(LogEntry log) => throw new OperationCanceledException(ErrorMessage); public void SetOperationMemorySize(ulong newSize) => throw new OperationCanceledException(ErrorMessage); + public void SetOperationReturnData(ReadOnlyMemory returnData) => throw new OperationCanceledException(ErrorMessage); public void ReportMemoryChange(long offset, in ReadOnlySpan data) => throw new OperationCanceledException(ErrorMessage); public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) => throw new OperationCanceledException(ErrorMessage); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockReceiptsTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockReceiptsTracer.cs index 492c48205dd0..40af7558d9ce 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockReceiptsTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockReceiptsTracer.cs @@ -24,6 +24,7 @@ public class BlockReceiptsTracer(bool parallel = false) : IBlockTracer, ITxTrace public bool IsTracingMemory => _currentTxTracer.IsTracingMemory; public bool IsTracingInstructions => _currentTxTracer.IsTracingInstructions; public bool IsTracingRefunds => _currentTxTracer.IsTracingRefunds; + public bool IsTracingReturnData => _currentTxTracer.IsTracingReturnData; public bool IsTracingCode => _currentTxTracer.IsTracingCode; public bool IsTracingStack => _currentTxTracer.IsTracingStack; public bool IsTracingState => _currentTxTracer.IsTracingState; @@ -166,6 +167,9 @@ public void ReportLog(LogEntry log) => public void SetOperationMemorySize(ulong newSize) => _currentTxTracer.SetOperationMemorySize(newSize); + public void SetOperationReturnData(ReadOnlyMemory returnData) => + _currentTxTracer.SetOperationReturnData(returnData); + public void ReportMemoryChange(long offset, in ReadOnlySpan data) => _currentTxTracer.ReportMemoryChange(offset, data); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxDirectStreamingTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxDirectStreamingTracer.cs index 4feb8bae47ea..646c34ebd221 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxDirectStreamingTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxDirectStreamingTracer.cs @@ -58,6 +58,10 @@ public sealed class GethLikeTxDirectStreamingTracer : GethLikeTxTracer private byte[]? _memoryBuffer; private int _memoryByteCount; + private byte[]? _returnDataBuffer; + private int _returnDataByteCount; + private byte[]? _returnDataHexBuffer; + private ArrayPoolList>? _storageByDepth; private int _activeStorageDepth; @@ -103,6 +107,7 @@ internal void ResetForNextTx(Transaction? transaction) _refundCheckpoints.Clear(); _stackByteCount = 0; _memoryByteCount = 0; + _returnDataByteCount = 0; if (_storageByDepth is not null) { for (int i = 0; i < _activeStorageDepth; i++) _storageByDepth[i].Clear(); @@ -137,6 +142,7 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe _gasCostAlreadySet = false; _stackByteCount = 0; _memoryByteCount = 0; + _returnDataByteCount = 0; } public override void ReportOperationRemainingGas(long gas) @@ -196,6 +202,18 @@ public override void SetOperationStorage(Address address, UInt256 storageIndex, top[storageIndex] = new UInt256(newValue, isBigEndian: true); } + public override void SetOperationReturnData(ReadOnlyMemory returnData) + { + if (!_hasPendingOpcode) return; + int needed = returnData.Length; + if (needed == 0) { _returnDataByteCount = 0; return; } + + // The source buffer is reused across opcodes, so copy the bytes into our own scratch. + EnsureBuffer(ref _returnDataBuffer, needed); + returnData.Span.CopyTo(_returnDataBuffer.AsSpan(0, needed)); + _returnDataByteCount = needed; + } + public override GethLikeTxTrace BuildResult() { FinalizePendingOpcode(); @@ -246,6 +264,8 @@ private void ReturnPooledBuffers() { if (_stackBuffer is not null) { ArrayPool.Shared.Return(_stackBuffer); _stackBuffer = null; } if (_memoryBuffer is not null) { ArrayPool.Shared.Return(_memoryBuffer); _memoryBuffer = null; } + if (_returnDataBuffer is not null) { ArrayPool.Shared.Return(_returnDataBuffer); _returnDataBuffer = null; } + if (_returnDataHexBuffer is not null) { ArrayPool.Shared.Return(_returnDataHexBuffer); _returnDataHexBuffer = null; } if (_storageByDepth is not null) { for (int i = 0; i < _storageByDepth.Count; i++) _storageByDepth[i].Dispose(); @@ -279,10 +299,30 @@ private void WriteOpcodeJson() if (IsTracingStack) WriteStackArrayIfPresent(); if (IsTracingFullMemory) WriteMemoryArrayIfPresent(); if (IsTracingOpLevelStorage) WriteStorageObjectIfPresent(); + if (IsTracingReturnData && _returnDataByteCount > 0) WriteReturnDataValue(); _writer.WriteEndObject(); } + private void WriteReturnDataValue() + { + // Encode "0x"-prefixed hex straight into a pooled scratch buffer and emit it as a raw JSON + // string, avoiding the intermediate string allocation on every traced opcode after a call. + int hexLength = _returnDataByteCount * 2; + int tokenLength = hexLength + 4; // quotes + "0x" + EnsureBuffer(ref _returnDataHexBuffer, tokenLength); + + Span token = _returnDataHexBuffer.AsSpan(0, tokenLength); + token[0] = (byte)'"'; + token[1] = (byte)'0'; + token[2] = (byte)'x'; + _returnDataBuffer.AsSpan(0, _returnDataByteCount).OutputBytesToByteHex(token.Slice(3, hexLength), extraNibble: false); + token[tokenLength - 1] = (byte)'"'; + + _writer.WritePropertyName("returnData"u8); + _writer.WriteRawValue(token, skipInputValidation: true); + } + private void WriteStackArrayIfPresent() { _writer.WriteStartArray("stack"u8); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxMemoryTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxMemoryTracer.cs index b75414910c34..38dd4ef309d3 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxMemoryTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxMemoryTracer.cs @@ -89,6 +89,12 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe } } + public override void SetOperationReturnData(ReadOnlyMemory returnData) + { + if (CurrentTraceEntry is not null && !returnData.IsEmpty) + CurrentTraceEntry.ReturnData = returnData.Span.ToHexString(true); + } + public override void ReportRefund(long refund) => _refund += refund; public override void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxTracer.cs index 32ede63a1121..e7383164a038 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxTracer.cs @@ -19,6 +19,7 @@ protected GethLikeTxTracer(GethTraceOptions options) IsTracingOpLevelStorage = !options.DisableStorage; IsTracingStack = !options.DisableStack; IsTracingFullMemory = options.EnableMemory; + IsTracingReturnData = options.EnableReturnData; IsTracing = IsTracing || IsTracingFullMemory; } diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethTraceOptions.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethTraceOptions.cs index a3de378dfb31..778c9091c7b9 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethTraceOptions.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethTraceOptions.cs @@ -20,6 +20,8 @@ public record GethTraceOptions public bool EnableMemory { get; init; } + public bool EnableReturnData { get; init; } + public bool DisableStack { get; init; } [JsonConverter(typeof(CustomTimeDurationConverter))] diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethTxTraceEntry.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethTxTraceEntry.cs index 828d65ff6117..98aee035a84c 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethTxTraceEntry.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethTxTraceEntry.cs @@ -42,5 +42,9 @@ public GethTxTraceEntry() public Dictionary? Storage { get; set; } + [JsonPropertyName("returnData")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? ReturnData { get; set; } + internal virtual void UpdateMemorySize(ulong size) { } } diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index b3ef0a1ef4f3..65a65b4338bd 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -409,6 +409,7 @@ public class MyTracer : ITxTracer, IDisposable public bool IsTracingDetailedMemory { get; set; } = true; public bool IsTracingInstructions => true; public bool IsTracingRefunds { get; } = false; + public bool IsTracingReturnData { get; } = false; public bool IsTracingCode => true; public bool IsTracingStack { get; set; } = true; public bool IsTracingState => false; @@ -466,6 +467,10 @@ public void SetOperationMemorySize(ulong newSize) { } + public void SetOperationReturnData(ReadOnlyMemory returnData) + { + } + public void ReportMemoryChange(long offset, in ReadOnlySpan data) { } diff --git a/src/Nethermind/Nethermind.Evm.Test/Tracing/GethLikeTxDirectStreamingTracerTests.cs b/src/Nethermind/Nethermind.Evm.Test/Tracing/GethLikeTxDirectStreamingTracerTests.cs index c4aae67f577c..0781722405c2 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Tracing/GethLikeTxDirectStreamingTracerTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Tracing/GethLikeTxDirectStreamingTracerTests.cs @@ -46,16 +46,42 @@ public void Streams_journaled_refund_counter(bool clearingFrameReverts) private static long? RefundAt(IEnumerable logs, string opcode) => logs.Single(l => l.Op == opcode).Refund; - private List ExecuteAndStream(bool clearingFrameReverts) + [Test] + public void Streams_returndata_when_enabled() + { + const string word = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"; + + List logs = StreamLogs(ReturnDataCallCode(word), GethTraceOptions.Default with { EnableReturnData = true }); + + using (Assert.EnterMultipleScope()) + { + Assert.That(logs.Last(l => l.Op == "CALL").ReturnData, Is.Null, "no return data before the call executes"); + Assert.That(logs.Last(l => l.Op == "STOP" && l.Depth == 1).ReturnData, Is.EqualTo($"0x{word}")); + } + } + + [Test] + public void Omits_returndata_when_not_enabled() + { + const string word = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"; + + List logs = StreamLogs(ReturnDataCallCode(word), GethTraceOptions.Default); + + Assert.That(logs.All(l => l.ReturnData is null), Is.True); + } + + private List ExecuteAndStream(bool clearingFrameReverts) => + StreamLogs(clearingFrameReverts ? RevertingCallCode() : ClearingSstoreCode(), GethTraceOptions.Default); + + private List StreamLogs(byte[] code, GethTraceOptions options) { - byte[] code = clearingFrameReverts ? RevertingCallCode() : ClearingSstoreCode(); (Block block, Transaction transaction) = PrepareTx(Activation, 100000, code); using MemoryStream stream = new(); using (Utf8JsonWriter writer = new(stream)) { writer.WriteStartArray(); - GethLikeTxDirectStreamingTracer tracer = new(transaction, GethTraceOptions.Default, writer, null, CancellationToken.None); + GethLikeTxDirectStreamingTracer tracer = new(transaction, options, writer, null, CancellationToken.None); _processor.Execute(transaction, new BlockExecutionContext(block.Header, SpecProvider.GetSpec(block.Header)), tracer); tracer.BuildResult(); writer.WriteEndArray(); @@ -65,7 +91,25 @@ private List ExecuteAndStream(bool clearingFrameReverts) return [.. document.RootElement.EnumerateArray().Select(static e => new StructLog( e.GetProperty("op").GetString()!, e.GetProperty("depth").GetInt32(), - e.TryGetProperty("refund", out JsonElement refund) ? refund.GetInt64() : null))]; + e.TryGetProperty("refund", out JsonElement refund) ? refund.GetInt64() : null, + e.TryGetProperty("returnData", out JsonElement returnData) ? returnData.GetString() : null))]; + } + + private byte[] ReturnDataCallCode(string word) + { + byte[] calleeCode = Prepare.EvmCode + .StoreDataInMemory(0, word) + .Return(32, 0) + .Done; + + TestState.CreateAccount(TestItem.AddressC, 1.Ether); + TestState.InsertCode(TestItem.AddressC, calleeCode, Spec); + TestState.Commit(Spec); + + return Prepare.EvmCode + .Call(TestItem.AddressC, 50000) + .Op(Instruction.STOP) + .Done; } private byte[] ClearingSstoreCode() @@ -100,5 +144,5 @@ private byte[] RevertingCallCode() .Done; } - private readonly record struct StructLog(string Op, int Depth, long? Refund); + private readonly record struct StructLog(string Op, int Depth, long? Refund, string? ReturnData); } diff --git a/src/Nethermind/Nethermind.Evm.Test/Tracing/GethLikeTxMemoryTracerTests.cs b/src/Nethermind/Nethermind.Evm.Test/Tracing/GethLikeTxMemoryTracerTests.cs index 20076dab1f0d..6fef622fc3a0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Tracing/GethLikeTxMemoryTracerTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Tracing/GethLikeTxMemoryTracerTests.cs @@ -495,6 +495,62 @@ public void Refund_is_rolled_back_when_frame_reverts() } } + [Test] + public void Can_trace_returndata_when_enabled() + { + const string word = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"; + + byte[] calleeCode = Prepare.EvmCode + .StoreDataInMemory(0, word) + .Return(32, 0) + .Done; + + TestState.CreateAccount(TestItem.AddressC, 1.Ether); + TestState.InsertCode(TestItem.AddressC, calleeCode, Spec); + TestState.Commit(Spec); + + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressC, 50000) + .Op(Instruction.STOP) + .Done; + + GethLikeTxTrace trace = ExecuteAndTrace(GethTraceOptions.Default with { EnableReturnData = true }, code); + + GethTxTraceEntry call = trace.Entries.Last(e => e.Opcode == "CALL"); + GethTxTraceEntry topLevelStop = trace.Entries.Last(e => e.Opcode == "STOP" && e.Depth == 1); + + using (Assert.EnterMultipleScope()) + { + Assert.That(call.ReturnData, Is.Null, "no return data before the call executes"); + Assert.That(topLevelStop.ReturnData, Is.EqualTo($"0x{word}"), "return data visible after the inner call returns"); + } + } + + [Test] + public void ReturnData_is_omitted_when_not_enabled() + { + const string word = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"; + + byte[] calleeCode = Prepare.EvmCode + .StoreDataInMemory(0, word) + .Return(32, 0) + .Done; + + TestState.CreateAccount(TestItem.AddressC, 1.Ether); + TestState.InsertCode(TestItem.AddressC, calleeCode, Spec); + TestState.Commit(Spec); + + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressC, 50000) + .Op(Instruction.STOP) + .Done; + + // Default options leave EnableReturnData off, so the field must never be populated. + GethLikeTxTrace trace = ExecuteAndTrace(code); + + Assert.That(trace.Entries.All(e => e.ReturnData is null), Is.True); + } + private static void AssertEntry(GethTxTraceEntry entry, long expectedPc, string expectedOpcode, string expectedStackTop, int expectedStackCount) { using (Assert.EnterMultipleScope()) diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 881b4d79b911..87dc610c54c0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -87,6 +87,14 @@ protected GethLikeTxTrace ExecuteAndTrace(params byte[] code) return tracer.BuildResult(); } + protected GethLikeTxTrace ExecuteAndTrace(GethTraceOptions options, params byte[] code) + { + (Block block, Transaction transaction) = PrepareTx(Activation, 100000, code); + GethLikeTxMemoryTracer tracer = new(transaction, options); + _processor.Execute(transaction, new BlockExecutionContext(block.Header, SpecProvider.GetSpec(block.Header)), tracer); + return tracer.BuildResult(); + } + protected GethLikeTxTrace ExecuteAndTrace(long blockNumber, long gasLimit, params byte[] code) { (Block block, Transaction transaction) = PrepareTx((blockNumber, Timestamp), gasLimit, code); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs index 35690c1537a8..85d74026c974 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs @@ -19,6 +19,7 @@ public class CancellationTxTracer(ITxTracer innerTracer, CancellationToken token private readonly bool _isTracingMemory; private readonly bool _isTracingInstructions; private readonly bool _isTracingRefunds; + private readonly bool _isTracingReturnData; private readonly bool _isTracingCode; private readonly bool _isTracingStack; private readonly bool _isTracingState; @@ -69,6 +70,12 @@ public bool IsTracingRefunds init => _isTracingRefunds = value; } + public bool IsTracingReturnData + { + get => _isTracingReturnData || innerTracer.IsTracingReturnData; + init => _isTracingReturnData = value; + } + public bool IsTracingCode { get => _isTracingCode || innerTracer.IsTracingCode; @@ -280,6 +287,15 @@ public void SetOperationMemorySize(ulong newSize) } } + public void SetOperationReturnData(ReadOnlyMemory returnData) + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingReturnData) + { + innerTracer.SetOperationReturnData(returnData); + } + } + public void ReportMemoryChange(long offset, in ReadOnlySpan data) { token.ThrowIfCancellationRequested(); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs index c59c0cc9b25b..7d35735b35bc 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -31,6 +31,7 @@ public CompositeTxTracer(IList txTracers) IsTracingMemory |= t.IsTracingMemory; IsTracingInstructions |= t.IsTracingInstructions; IsTracingRefunds |= t.IsTracingRefunds; + IsTracingReturnData |= t.IsTracingReturnData; IsTracingCode |= t.IsTracingCode; IsTracingStack |= t.IsTracingStack; IsTracingBlockHash |= t.IsTracingBlockHash; @@ -49,6 +50,7 @@ public CompositeTxTracer(IList txTracers) public bool IsTracingMemory { get; } public bool IsTracingInstructions { get; } public bool IsTracingRefunds { get; } + public bool IsTracingReturnData { get; } public bool IsTracingCode { get; } public bool IsTracingStack { get; } public bool IsTracingBlockHash { get; } @@ -272,6 +274,18 @@ public void SetOperationMemorySize(ulong newSize) } } + public void SetOperationReturnData(ReadOnlyMemory returnData) + { + for (int index = 0; index < _txTracers.Count; index++) + { + ITxTracer innerTracer = _txTracers[index]; + if (innerTracer.IsTracingReturnData) + { + innerTracer.SetOperationReturnData(returnData); + } + } + } + public void ReportMemoryChange(long offset, in ReadOnlySpan data) { for (int index = 0; index < _txTracers.Count; index++) diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs index 1e0b91496bd2..d81f63b30d83 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs @@ -42,6 +42,7 @@ public enum DebugPhase { Starting, Blocked, Running, Aborted } public bool IsTracingInstructions => InnerTracer.IsTracingInstructions; public bool IsTracingRefunds => InnerTracer.IsTracingRefunds; + public bool IsTracingReturnData => InnerTracer.IsTracingReturnData; public bool IsTracingCode => InnerTracer.IsTracingCode; @@ -215,6 +216,9 @@ public void SetOperationMemory(TraceMemory memoryTrace) public void SetOperationMemorySize(ulong newSize) => InnerTracer.SetOperationMemorySize(newSize); + public void SetOperationReturnData(ReadOnlyMemory returnData) + => InnerTracer.SetOperationReturnData(returnData); + public void ReportMemoryChange(long offset, in ReadOnlySpan data) => InnerTracer.ReportMemoryChange(offset, data); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs index 773933475b1c..b91aa7facf43 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs @@ -83,6 +83,15 @@ public interface ITxTracer : IWorldStateTracer, IDisposable /// bool IsTracingRefunds { get; } + /// + /// Per-opcode return-data buffer (the output of the most recent inner call/create). + /// + /// + /// Controls + /// - + /// + bool IsTracingReturnData { get; } + /// /// Code deployment /// @@ -143,6 +152,7 @@ public interface ITxTracer : IWorldStateTracer, IDisposable || IsTracingMemory || IsTracingInstructions || IsTracingRefunds + || IsTracingReturnData || IsTracingCode || IsTracingStack || IsTracingBlockHash @@ -245,6 +255,14 @@ public interface ITxTracer : IWorldStateTracer, IDisposable /// Depends on void SetOperationMemorySize(ulong newSize); + /// + /// Reports the return-data buffer visible to the current opcode (the output of the most + /// recent inner call or create). + /// + /// The current return-data buffer. + /// Depends on + void SetOperationReturnData(ReadOnlyMemory returnData); + /// /// /// diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index d7b2f5cabb13..25e988db29d1 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs @@ -22,6 +22,7 @@ public bool IsTracing || IsTracingMemory || IsTracingInstructions || IsTracingRefunds + || IsTracingReturnData || IsTracingCode || IsTracingStack || IsTracingBlockHash @@ -38,6 +39,7 @@ public bool IsTracing public virtual bool IsTracingMemory { get; protected set; } public virtual bool IsTracingInstructions { get; protected set; } public virtual bool IsTracingRefunds { get; protected set; } + public virtual bool IsTracingReturnData { get; protected set; } public virtual bool IsTracingCode { get; protected set; } public virtual bool IsTracingStack { get; protected set; } public virtual bool IsTracingBlockHash { get; protected set; } @@ -62,6 +64,7 @@ public virtual void SetOperationStack(TraceStack stack) { } public virtual void ReportStackPush(in ReadOnlySpan stackItem) { } public virtual void SetOperationMemory(TraceMemory memoryTrace) { } public virtual void SetOperationMemorySize(ulong newSize) { } + public virtual void SetOperationReturnData(ReadOnlyMemory returnData) { } public virtual void ReportMemoryChange(long offset, in ReadOnlySpan data) { } public virtual void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) { } public virtual void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) { } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3dff3674a4de..1bee9868cac6 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1432,6 +1432,11 @@ private void StartInstructionTrace(Instruction instruction, long gasAvailable, i { _txTracer.SetOperationStack(new TraceStack(vmState.MemoryStacks(stackValue.Head))); } + + if (_txTracer.IsTracingReturnData) + { + _txTracer.SetOperationReturnData(ReturnDataBuffer); + } } [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs index 51b4d78aa73b..c14fef1a4b6b 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs @@ -26,6 +26,7 @@ public class StateTestTxTracer : ITxTracer, IDisposable public bool IsTracingDetailedMemory { get; set; } = true; public bool IsTracingInstructions => true; public bool IsTracingRefunds { get; } = false; + public bool IsTracingReturnData { get; } = false; public bool IsTracingCode => false; public bool IsTracingStack { get; set; } = true; public bool IsTracingState => false; @@ -149,6 +150,10 @@ public void SetOperationMemorySize(ulong newSize) } } + public void SetOperationReturnData(ReadOnlyMemory returnData) + { + } + public void ReportMemoryChange(long offset, in ReadOnlySpan data) { }