Skip to content

Commit

Permalink
Javascript Tracer resource leak (#6929)
Browse files Browse the repository at this point in the history
Co-authored-by: lukasz.rozmej <[email protected]>
  • Loading branch information
Marchhill and LukaszRozmej authored Apr 18, 2024
1 parent 6eda258 commit cad37a7
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 63 deletions.
35 changes: 28 additions & 7 deletions src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Nethermind.Consensus.Processing;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;
using Nethermind.Crypto;
using Nethermind.Evm.Tracing;
Expand Down Expand Up @@ -120,8 +121,15 @@ public GethLikeTxTrace Trace(Hash256 blockHash, int txIndex, GethTraceOptions op

block = block.WithReplacedBodyCloned(BlockBody.WithOneTransactionOnly(tx));
IBlockTracer<GethLikeTxTrace> blockTracer = CreateOptionsTracer(block.Header, options with { TxHash = tx.Hash });
_processor.Process(block, ProcessingOptions.Trace, blockTracer.WithCancellation(cancellationToken));
return blockTracer.BuildResult().SingleOrDefault();
try
{
_processor.Process(block, ProcessingOptions.Trace, blockTracer.WithCancellation(cancellationToken));
return blockTracer.BuildResult().SingleOrDefault();
}
finally
{
blockTracer.TryDispose();
}
}

public IReadOnlyCollection<GethLikeTxTrace> TraceBlock(BlockParameter blockParameter, GethTraceOptions options, CancellationToken cancellationToken)
Expand Down Expand Up @@ -164,9 +172,15 @@ public IEnumerable<string> TraceBlockToFile(Hash256 blockHash, GethTraceOptions

IBlockTracer<GethLikeTxTrace> tracer = CreateOptionsTracer(block.Header, options with { TxHash = txHash });

_processor.Process(block, ProcessingOptions.Trace, tracer.WithCancellation(cancellationToken));

return tracer.BuildResult().SingleOrDefault();
try
{
_processor.Process(block, ProcessingOptions.Trace, tracer.WithCancellation(cancellationToken));
return tracer.BuildResult().SingleOrDefault();
}
finally
{
tracer.TryDispose();
}
}

private IBlockTracer<GethLikeTxTrace> CreateOptionsTracer(BlockHeader block, GethTraceOptions options) =>
Expand All @@ -193,8 +207,15 @@ private IReadOnlyCollection<GethLikeTxTrace> TraceBlock(Block? block, GethTraceO
}

IBlockTracer<GethLikeTxTrace> tracer = CreateOptionsTracer(block.Header, options);
_processor.Process(block, ProcessingOptions.Trace, tracer.WithCancellation(cancellationToken));
return tracer.BuildResult();
try
{
_processor.Process(block, ProcessingOptions.Trace, tracer.WithCancellation(cancellationToken));
return tracer.BuildResult();
}
finally
{
tracer.TryDispose();
}
}

private static Block GetBlockToTrace(Rlp blockRlp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ public class GethLikeJavaScriptTracerTests : VirtualMachineTestsBase
[TestCase("{ fault: function(log, db) { } }", TestName = "result")]
[TestCase("{ fault: function(log, db) { }, result: function(ctx, db) { return null }, enter: function(frame) { } }", TestName = "exit")]
[TestCase("{ fault: function(log, db) { }, result: function(ctx, db) { return null }, exit: function(frame) { } }", TestName = "enter")]
public void missing_functions(string tracer)
public void missing_functions(string tracerCode)
{
Action trace = () => ExecuteBlock(GetTracer(tracer), MStore());
using GethLikeBlockJavaScriptTracer tracer = GetTracer(tracerCode);
Action trace = () => ExecuteBlock(tracer, MStore());
trace.Should().Throw<ArgumentException>();
}

Expand All @@ -39,11 +40,11 @@ public void log_operations()
fault: function(log, db) { this.retVal.push('FAULT: ' + JSON.stringify(log)) },
result: function(ctx, db) { return this.retVal }
}";
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
string[] expectedStrings = { "0:PUSH32:0:79000:0", "33:PUSH1:0:78997:0", "35:MSTORE:0:78994:0", "36:PUSH32:0:78988:0", "69:PUSH1:0:78985:0", "71:MSTORE:0:78982:0", "72:STOP:0:78976:0" };
traces.CustomTracerResult?.Value.Should().BeEquivalentTo(expectedStrings);
}
Expand All @@ -60,11 +61,11 @@ public void log_operation_functions()
fault: function(log, db) { this.retVal.push('FAULT: ' + JSON.stringify(log)) },
result: function(ctx, db) { return this.retVal }
}";
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
string[] expectedStrings = { "PUSH32 : 127 : true", "PUSH1 : 96 : true", "MSTORE : 82 : false", "PUSH32 : 127 : true", "PUSH1 : 96 : true", "MSTORE : 82 : false", "STOP : 0 : false" };
traces.CustomTracerResult?.Value.Should().BeEquivalentTo(expectedStrings);
}
Expand All @@ -78,11 +79,11 @@ public void log_stack_functions()
fault: function(log, db) { this.retVal.push('FAULT: ' + JSON.stringify(log)) },
result: function(ctx, db) { return this.retVal }
}";
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
int[] expected = { 0, 1, 2, 0, 1, 2, 0 };
traces.CustomTracerResult?.Value.Should().BeEquivalentTo(expected);
}
Expand All @@ -102,11 +103,11 @@ public void log_memory_functions()
fault: function(log, db) { this.retVal.push('FAULT: ' + JSON.stringify(log.getError())) },
result: function(ctx, db) { return this.retVal }
}";
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
int[] expectedResult = { 0, 32, 64 };
traces.CustomTracerResult?.Value.Should().BeEquivalentTo(expectedResult);
}
Expand All @@ -120,11 +121,11 @@ public void log_contract_functions()
fault: function(log, db) { this.retVal.push('FAULT: ' + JSON.stringify(log)) },
result: function(ctx, db) { return this.retVal }
}";
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
traces.CustomTracerResult?.Value.Should().BeEquivalentTo("942921b14f1b1c385cd7e0cc2ef7abe5598c8358:b7705ae4c6f81b66cdb323c65f4e8133690fc099:");
}

Expand All @@ -139,11 +140,11 @@ public void Js_traces_simple_filter()
}";
;

GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
string[] expectedStrings = { "0:PUSH32", "33:PUSH1", "35:MSTORE", "36:PUSH32", "69:PUSH1", "71:MSTORE", "72:STOP" };
Assert.That(traces.CustomTracerResult?.Value, Is.EqualTo(expectedStrings));
}
Expand All @@ -163,11 +164,11 @@ public void filter_with_conditionals()
fault: function(log, db) { this.retVal.push('FAULT: ' + JSON.stringify(log)); },
result: function(ctx, db) { return this.retVal; }
}";
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
string[] expectedStrings = { "33: PUSH1", "35: MSTORE", "69: PUSH1", "71: MSTORE" };
Assert.That(traces.CustomTracerResult?.Value, Is.EqualTo(expectedStrings));
}
Expand All @@ -192,11 +193,11 @@ public void storage_information()
return this.retVal;
}
}";
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
SStore_double(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
string[] expectedStrings = { "35: SSTORE 0", "71: SSTORE 20", "107: SLOAD 0", "108: STOP a01234 <- a01234" };
Assert.That(traces.CustomTracerResult?.Value, Is.EqualTo(expectedStrings));
}
Expand Down Expand Up @@ -228,11 +229,11 @@ public void operation_results()
}
}
""";
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
SStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
string[] expectedStrings = { "68 SSTORE 1 <- a01234", "104 SLOAD 1", "Result: a01234" };
Assert.That(traces.CustomTracerResult?.Value, Is.EqualTo(expectedStrings));
}
Expand Down Expand Up @@ -295,69 +296,69 @@ public void calls_btn_contracts()
}
""";

GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(userTracer),
SStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
string[] expectedStrings = { "68: SSTORE 942921b14f1b1c385cd7e0cc2ef7abe5598c8358:1 <- a01234", "104: SLOAD 942921b14f1b1c385cd7e0cc2ef7abe5598c8358:1", "Result: 1" };
Assert.That(traces.CustomTracerResult?.Value, Is.EqualTo(expectedStrings));
}

[Test]
public void noop_tracer_legacy()
{
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer("noopTracer"),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
Assert.That(traces.CustomTracerResult?.Value, Has.All.Empty);
}

[Test]
public void opcount_tracer()
{
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer("opcountTracer"),
MStore(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();
Assert.That(traces.CustomTracerResult?.Value, Is.EqualTo(7));
}

[Test]
public void prestate_tracer()
{
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer("prestateTracer_legacy"),
NestedCalls(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();

Assert.That(JsonSerializer.Serialize(traces.CustomTracerResult?.Value), Is.EqualTo("{\"942921b14f1b1c385cd7e0cc2ef7abe5598c8358\":{\"balance\":\"0x56bc75e2d63100000\",\"nonce\":0,\"code\":\"60006000600060007376e68a8696537e4141926f3e528733af9e237d6961c350f400\",\"storage\":{}},\"76e68a8696537e4141926f3e528733af9e237d69\":{\"balance\":\"0xde0b6b3a7640000\",\"nonce\":0,\"code\":\"7f7f000000000000000000000000000000000000000000000000000000000000006000527f0060005260036000f30000000000000000000000000000000000000000000000602052602960006000f000\",\"storage\":{}},\"89aa9b2ce05aaef815f25b237238c0b4ffff6ae3\":{\"balance\":\"0x0\",\"nonce\":0,\"code\":\"\",\"storage\":{}},\"b7705ae4c6f81b66cdb323c65f4e8133690fc099\":{\"balance\":\"0x56bc75e2d63100000\",\"nonce\":0,\"code\":\"\",\"storage\":{}}}"));
}

[Test]
public void call_tracer()
{
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer("callTracer"),
NestedCalls(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.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(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer("4byteTracer_legacy"),
CallWithInput(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();

Assert.That(JsonSerializer.Serialize(traces.CustomTracerResult?.Value), Is.EqualTo("{\"00000000-1\":2,\"00000000-2\":1}"));
}
Expand Down Expand Up @@ -450,23 +451,23 @@ private byte[] CallWithInput()
[Test]
public void complex_tracer()
{
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(ComplexTracer),
Array.Empty<byte>(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();

TestContext.WriteLine(GetEthereumJsonSerializer().Serialize(traces.CustomTracerResult));
}

[Test]
public void complex_tracer_nested_call()
{
GethLikeTxTrace traces = ExecuteBlock(
using GethLikeBlockJavaScriptTracer tracer = ExecuteBlock(
GetTracer(ComplexTracer),
NestedCalls(),
MainnetSpecProvider.CancunActivation)
.BuildResult().First();
MainnetSpecProvider.CancunActivation);
GethLikeTxTrace traces = tracer.BuildResult().First();

TestContext.WriteLine(GetEthereumJsonSerializer().Serialize(traces.CustomTracerResult));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ public override void EndBlockTrace()
{
base.EndBlockTrace();
Engine.CurrentEngine = null;
ArrayPoolList<IDisposable>? list = Interlocked.Exchange(ref _engines, null);
list?.Dispose();
}

protected override bool ShouldTraceTx(Transaction? tx) => base.ShouldTraceTx(tx) && tx is not null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@ public async Task Will_Trigger_ReorgBoundaryEvent_On_Prune()

if (i > 4)
{
Assert.That(() => reorgBoundary, Is.EqualTo(i - 3).After(5000, 1));
Assert.That(() => reorgBoundary, Is.EqualTo(i - 3).After(10000, 1));
}
else
{
Expand Down

0 comments on commit cad37a7

Please sign in to comment.