Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

t8n test #7744

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Globalization;
using Nethermind.Core;
using Nethermind.JsonRpc.Data;
using System.Text.Json;
using System.Text.Json.Serialization;
using Nethermind.Core.Crypto;
using Nethermind.Int256;
using Nethermind.Serialization.Json;

namespace Nethermind.JsonRpc.Converters;

Expand All @@ -22,39 +18,6 @@ public class TxReceiptConverter : JsonConverter<TxReceipt>

public override void Write(Utf8JsonWriter writer, TxReceipt value, JsonSerializerOptions options)
{
writer.WriteStartObject();
var receipt = new ReceiptForRpc(value.TxHash!, value, default);
if (receipt.Type != TxType.Legacy)
{
writer.WritePropertyName("type");
JsonSerializer.Serialize(writer, receipt.Type, options);
}
writer.WritePropertyName("root");
ByteArrayConverter.Convert(writer, (receipt.Root ?? Keccak.Zero).Bytes);
writer.WritePropertyName("status");
ForcedNumberConversion.ForcedConversion.Value = NumberConversion.Hex;
JsonSerializer.Serialize(writer, receipt.Status, options);

writer.WritePropertyName("cumulativeGasUsed");
JsonSerializer.Serialize(writer, receipt.CumulativeGasUsed, options);
writer.WritePropertyName("effectiveGasPrice");
JsonSerializer.Serialize(writer, receipt.EffectiveGasPrice, options);
writer.WritePropertyName("logsBloom");
JsonSerializer.Serialize(writer, receipt.LogsBloom, options);
writer.WritePropertyName("logs");
JsonSerializer.Serialize(writer, receipt.Logs.Length == 0 ? null : receipt.Logs, options);
writer.WritePropertyName("transactionHash");
JsonSerializer.Serialize(writer, receipt.TransactionHash, options);
writer.WritePropertyName("contractAddress");
JsonSerializer.Serialize(writer, receipt.ContractAddress, options);
writer.WritePropertyName("gasUsed");
JsonSerializer.Serialize(writer, receipt.GasUsed, options);
writer.WritePropertyName("blockHash");
JsonSerializer.Serialize(writer, receipt.BlockHash ?? Hash256.Zero, options);

writer.WritePropertyName("transactionIndex");
JsonSerializer.Serialize(writer, UInt256.Parse(receipt.TransactionIndex.ToString(), NumberStyles.Integer), options);

writer.WriteEndObject();
JsonSerializer.Serialize(writer, new ReceiptForRpc(value.TxHash!, value, default), options);
}
}
4 changes: 2 additions & 2 deletions src/Nethermind/Nethermind.JsonRpc/Data/ReceiptForRpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public ReceiptForRpc(Hash256 txHash, TxReceipt receipt, TxGasInfo gasInfo, int l
BlobGasPrice = gasInfo.BlobGasPrice;
From = receipt.Sender;
To = receipt.Recipient;
ContractAddress = receipt.ContractAddress;
ContractAddress = receipt.ContractAddress ?? Address.Zero;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we serialize Address.Zero for transactions that don't create contracts?

This will also affect all the RPC method, are we sure we want to do it?

If we don't want to change RPC methods, but we want to change it here, you can inherit ReceiptForRpc and add this logic in the derived class.

Logs = (receipt.Logs ?? []).Select((l, idx) => new LogEntryForRpc(receipt, l, idx + logIndexStart)).ToArray();
LogsBloom = receipt.Bloom;
Root = receipt.PostTransactionState;
Expand Down Expand Up @@ -58,7 +58,7 @@ public ReceiptForRpc(Hash256 txHash, TxReceipt receipt, TxGasInfo gasInfo, int l
public Address To { get; set; }

[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public Address? ContractAddress { get; set; }
public Address ContractAddress { get; set; }
public LogEntryForRpc[] Logs { get; set; }
public Bloom? LogsBloom { get; set; }
public Hash256? Root { get; set; }
Expand Down
34 changes: 34 additions & 0 deletions tools/Evm/Evm.Test/Evm.Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="NUnit" Version="4.2.2"/>
<PackageReference Include="NUnit.Analyzers" Version="3.9.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Evm\Evm.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="testdata\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
260 changes: 260 additions & 0 deletions tools/Evm/Evm.Test/T8nTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Evm.T8n;
using Evm.T8n.JsonTypes;
using Nethermind.Logging;
using Nethermind.Serialization.Json;
using Newtonsoft.Json.Linq;

namespace Evm.Test;

public class InputParams(string basedir, string alloc, string env, string txs, string stateFork, string? stateReward = null)
{
public readonly string Alloc = Path.Combine(basedir, alloc);
public readonly string Env = Path.Combine(basedir, env);
public readonly string Txs = Path.Combine(basedir, txs);
public readonly string StateFork = stateFork;
public readonly string? StateReward = stateReward;
}

public class OutputParams(string? alloc = null, string? result = null, string? body = null)
{
public readonly string? Alloc = alloc;
public readonly string? Result = result;
public readonly string? Body = body;
}

public class T8nTests
{
private readonly EthereumJsonSerializer _ethereumJsonSerializer = new();

[Test]
public void Test1()
{
Execute(
new InputParams("testdata/1/", "alloc.json", "env.json", "txs.json", "Frontier+1346"),
new OutputParams(alloc: "stdout", result: "stdout"),
expectedExitCode: 3);
}

[Test]
public void Test2()
{
Execute(
new InputParams("testdata/1/", "alloc.json", "env.json", "txs.json", "Byzantium"),
new OutputParams(alloc: "stdout", result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/1/exp.json");
}

[Test]
public void Test3()
{
Execute(
new InputParams("testdata/3/", "alloc.json", "env.json", "txs.json", "Berlin"),
new OutputParams(alloc: "stdout", result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/3/exp.json");
}

[Test]
public void Test4()
{
Execute(
new InputParams("testdata/4/", "alloc.json", "env.json", "txs.json", "Berlin"),
new OutputParams(alloc: "stdout", result: "stdout"),
expectedExitCode: 4);
}

[Test]
public void Test5()
{
Execute(
new("testdata/5/", "alloc.json", "env.json", "txs.json", "Byzantium", "0x80"),
new OutputParams(alloc: "stdout", result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/5/exp.json");
}

[Test]
public void Test6()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you name tests in some sensible way? I see it is based on testdata so Test13_txs_London?

{
Execute(
new InputParams("testdata/13/", "alloc.json", "env.json", "txs.json", "London"),
new OutputParams(body: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/13/exp.json");
}

[Test]
public void Test7()
{
Execute(
new InputParams("testdata/13/", "alloc.json", "env.json", "signed_txs.rlp", "London"),
new OutputParams(result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/13/exp2.json");
}

[Test]
public void Test8()
{
Execute(
new InputParams("testdata/14/", "alloc.json", "env.json", "txs.json", "London"),
new OutputParams(result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/14/exp.json");
}

[Test]
public void Test9()
{
Execute(
new InputParams("testdata/14/", "alloc.json", "env.uncles.json", "txs.json", "London"),
new OutputParams(result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/14/exp2.json");
}

[Test]
public void Test10()
{
Execute(
new InputParams("testdata/14/", "alloc.json", "env.uncles.json", "txs.json", "Berlin"),
new OutputParams(result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/14/exp_berlin.json");
}

[Test]
public void Test11()
{
Execute(
new InputParams("testdata/19/", "alloc.json", "env.json", "txs.json", "London"),
new OutputParams(result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/19/exp_london.json");
}

[Test]
public void Test12()
{
Execute(
new InputParams("testdata/19/", "alloc.json", "env.json", "txs.json", "ArrowGlacier"),
new OutputParams(result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/19/exp_arrowglacier.json");
}

[Test]
public void Test13()
{
Execute(
new InputParams("testdata/19/", "alloc.json", "env.json", "txs.json", "GrayGlacier"),
new OutputParams(result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/19/exp_grayglacier.json");
}

[Test]
public void Test14()
{
Execute(
new InputParams("testdata/23/", "alloc.json", "env.json", "txs.json", "Berlin"),
new OutputParams(result: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/23/exp.json");
}

[Test]
public void Test15()
{
Execute(
new InputParams("testdata/24/", "alloc.json", "env.json", "txs.json", "Merge"),
new OutputParams(result: "stdout", alloc: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/24/exp.json");
}

[Test]
public void Test16()
{
Execute(
new InputParams("testdata/24/", "alloc.json", "env-missingrandom.json", "txs.json", "Merge"),
new OutputParams(result: "stdout", alloc: "stdout"),
expectedExitCode: 3);
}

[Test]
public void Test17()
{
Execute(
new InputParams("testdata/26/", "alloc.json", "env.json", "txs.json", "Shanghai"),
new OutputParams(result: "stdout", alloc: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/26/exp.json");
}

[Test]
public void Test18()
{
Execute(
new InputParams("testdata/28/", "alloc.json", "env.json", "txs.rlp", "Cancun"),
new OutputParams(result: "stdout", alloc: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/28/exp.json");
}

[Test]
public void Test19()
{
Execute(
new InputParams("testdata/29/", "alloc.json", "env.json", "txs.json", "Cancun"),
new OutputParams(result: "stdout", alloc: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/29/exp.json");
}

[Test]
public void Test20()
{
Execute(
new InputParams("testdata/30/", "alloc.json", "env.json", "txs_more.rlp", "Cancun"),
new OutputParams(result: "stdout", alloc: "stdout"),
expectedExitCode: 0,
expectedOutputFile: "testdata/30/exp.json");
}

private void Execute(InputParams inputParams, OutputParams outputParams, int expectedExitCode, string? expectedOutputFile = null)
{
var arguments = new T8nCommandArguments
{
InputAlloc = inputParams.Alloc,
InputEnv = inputParams.Env,
InputTxs = inputParams.Txs,
OutputBody = outputParams.Body,
StateFork = inputParams.StateFork,
};
if (outputParams.Alloc is not null) arguments.OutputAlloc = outputParams.Alloc;
if (outputParams.Result is not null) arguments.OutputResult = outputParams.Result;
if (inputParams.StateReward is not null) arguments.StateReward = inputParams.StateReward;

T8nOutput output = T8nTool.Run(arguments, NullLogManager.Instance);

Assert.That(output.ExitCode, Is.EqualTo(expectedExitCode));

if (expectedOutputFile == null) return;

var outputString = _ethereumJsonSerializer.Serialize(output, true);
var fileContent = File.ReadAllText(expectedOutputFile);
Assert.That(AreEqual(fileContent, outputString));
}

private static bool AreEqual(string json1, string json2)
{
var expected = JToken.Parse(json1);
var actual = JToken.Parse(json2);
return JToken.DeepEquals(actual, expected);
}
}
12 changes: 12 additions & 0 deletions tools/Evm/Evm.Test/testdata/1/alloc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0x5ffd4878be161d74",
"code": "0x",
"nonce": "0xac",
"storage": {}
},
"0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{
"balance": "0xfeedbead",
"nonce" : "0x00"
}
}
7 changes: 7 additions & 0 deletions tools/Evm/Evm.Test/testdata/1/env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"currentDifficulty": "0x20000",
"currentGasLimit": "0x750a163df65e8a",
"currentNumber": "1",
"currentTimestamp": "1000"
}
Loading
Loading