Skip to content

Commit

Permalink
t8n input processing (#7690)
Browse files Browse the repository at this point in the history
  • Loading branch information
yerke26 authored Oct 31, 2024
1 parent efe8dba commit 65055f1
Show file tree
Hide file tree
Showing 14 changed files with 600 additions and 39 deletions.
8 changes: 8 additions & 0 deletions tools/Evm/Evm/Evm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@

<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<ProjectReference Include="..\..\..\src\Nethermind\Ethereum.Test.Base\Ethereum.Test.Base.csproj" />
<ProjectReference Include="..\..\..\src\Nethermind\Nethermind.Core\Nethermind.Core.csproj" />
<ProjectReference Include="..\..\..\src\Nethermind\Nethermind.Evm\Nethermind.Evm.csproj" />
<ProjectReference Include="..\..\..\src\Nethermind\Nethermind.Db\Nethermind.Db.csproj" />
<ProjectReference Include="..\..\..\src\Nethermind\Nethermind.Specs\Nethermind.Specs.csproj" />
<ProjectReference Include="..\..\..\src\Nethermind\Nethermind.Trie\Nethermind.Trie.csproj" />
<ProjectReference Include="..\..\..\src\Nethermind\Nethermind.Consensus\Nethermind.Consensus.csproj" />
<ProjectReference Include="..\..\..\src\Nethermind\Nethermind.Consensus.AuRa\Nethermind.Consensus.AuRa.csproj" />
</ItemGroup>

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

namespace Evm.t8n.Errors;

public class T8NErrorCodes
{
public const int ErrorEVM = 2; // Other EVM error
public const int ErrorConfig = 3; // Failed configuration: when a non-supported or invalid fork was specified.

public const int ErrorMissingBlockhash = 4; // Block history is not supplied, but needed for a BLOCKHASH operation. If BLOCKHASH is invoked targeting a block which history has not been provided for, the program will exit with code 4.

public const int ErrorJson = 10; // Invalid input json: the supplied data could not be marshalled
public const int ErrorIO = 11; // IO problems: failure to load or save files
public const int ErrorRlp = 12; // Invalid Rlp
}
26 changes: 26 additions & 0 deletions tools/Evm/Evm/t8n/Errors/T8NException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core.Exceptions;

namespace Evm.t8n.Errors;

public class T8NException : Exception, IExceptionWithExitCode
{
public T8NException(Exception e, int exitCode) : base(e.Message, e)
{
ExitCode = exitCode;
}

public T8NException(Exception e, string message, int exitCode) : base(message, e)
{
ExitCode = exitCode;
}

public T8NException(string message, int exitCode) : base(message)
{
ExitCode = exitCode;
}

public int ExitCode { get; }
}
43 changes: 43 additions & 0 deletions tools/Evm/Evm/t8n/JsonTypes/EnvJson.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Int256;

namespace Evm.t8n.JsonTypes;

public class EnvJson
{
public Address? CurrentCoinbase { get; set; }
public long CurrentGasLimit { get; set; }
public long CurrentNumber { get; set; }
public ulong CurrentTimestamp { get; set; }
public Withdrawal[]? Withdrawals { get; set; }

public UInt256? CurrentRandom { get; set; }
public ulong ParentTimestamp { get; set; }
public UInt256? ParentDifficulty { get; set; }
public UInt256? CurrentBaseFee { get; set; }
public UInt256? CurrentDifficulty { get; set; }
public Hash256? ParentUncleHash { get; set; }
public Hash256? ParentBeaconBlockRoot { get; set; }
public UInt256? ParentBaseFee { get; set; }
public long ParentGasUsed { get; set; }
public long ParentGasLimit { get; set; }
public ulong? ParentExcessBlobGas { get; set; }
public ulong? CurrentExcessBlobGas { get; set; }
public ulong? ParentBlobGasUsed { get; set; }

public Dictionary<string, Hash256> BlockHashes { get; set; } = [];
public Ommer[] Ommers { get; set; } = [];

public Hash256? GetCurrentRandomHash256()
{
if (CurrentRandom is null) return null;

Span<byte> bytes = stackalloc byte[32];
CurrentRandom?.ToBigEndian(bytes);
return new Hash256(bytes);
}
}
63 changes: 63 additions & 0 deletions tools/Evm/Evm/t8n/JsonTypes/InputData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Ethereum.Test.Base;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Crypto;
using Nethermind.Facade.Eth.RpcTransaction;
using Nethermind.Serialization.Rlp;

namespace Evm.t8n.JsonTypes;

public class InputData
{
public Dictionary<Address, AccountState>? Alloc { get; set; }
public EnvJson? Env { get; set; }
public TransactionForRpc[]? Txs { get; set; }
public TransactionMetaData[]? TransactionMetaDataList { get; set; }
public string? TxRlp { get; set; }

public Transaction[] GetTransactions(TxDecoder decoder)
{
List<Transaction> transactions = [];
if (TxRlp is not null)
{
RlpStream rlp = new(Bytes.FromHexString(TxRlp));
transactions = decoder.DecodeArray(rlp).ToList();
}
else if (Txs is not null && TransactionMetaDataList is not null)
{
for (int i = 0; i < Txs.Length; i++)
{
var transaction = Txs[i].ToTransaction();
transaction.SenderAddress = null; // t8n does not accept SenderAddress from input, so need to reset senderAddress

SignTransaction(transaction, TransactionMetaDataList[i], (LegacyTransactionForRpc) Txs[i]);

transaction.Hash = transaction.CalculateHash();
transactions.Add(transaction);
}
}

return transactions.ToArray();
}

private static void SignTransaction(Transaction transaction, TransactionMetaData transactionMetaData, LegacyTransactionForRpc txLegacy)
{
if (transactionMetaData.SecretKey is not null)
{
var privateKey = new PrivateKey(transactionMetaData.SecretKey);
transaction.SenderAddress = privateKey.Address;

EthereumEcdsa ecdsa = new(transaction.ChainId ?? TestBlockchainIds.ChainId);

ecdsa.Sign(privateKey, transaction, transactionMetaData.Protected ?? true);
}
else if (txLegacy.R.HasValue && txLegacy.S.HasValue && txLegacy.V.HasValue)
{
transaction.Signature = new Signature(txLegacy.R.Value, txLegacy.S.Value, txLegacy.V.Value.ToUInt64(null));
}
}
}
12 changes: 12 additions & 0 deletions tools/Evm/Evm/t8n/JsonTypes/Ommer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;

namespace Evm.t8n.JsonTypes;

public class Ommer(int delta, Address address)
{
public int Delta { get; set; } = delta;
public Address Address { get; set; } = address;
}
49 changes: 49 additions & 0 deletions tools/Evm/Evm/t8n/JsonTypes/T8NTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Ethereum.Test.Base;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Evm.Tracing.GethStyle;
using Nethermind.Int256;
using Nethermind.Specs;

namespace Evm.t8n.JsonTypes;

public class T8NTest(IReleaseSpec spec, ISpecProvider specProvider)
{
public IReleaseSpec Spec { get; set; } = spec;
public ISpecProvider SpecProvider { get; set; } = specProvider;
public Address? CurrentCoinbase { get; set; }
public UInt256? CurrentDifficulty { get; set; }

public UInt256? CurrentBaseFee { get; set; }
public long CurrentGasLimit { get; set; }
public long CurrentNumber { get; set; }
public ulong CurrentTimestamp { get; set; }
public Hash256? PreviousHash { get; set; }
public Dictionary<Address, AccountState> Alloc { get; set; } = [];
public Hash256? PostHash { get; set; }
public Transaction[] Transactions { get; set; } = [];
public Hash256? CurrentRandom { get; set; }
public Hash256? CurrentBeaconRoot { get; set; }
public Hash256? CurrentWithdrawalsRoot { get; set; }
public ulong? CurrentExcessBlobGas { get; set; }
public UInt256? ParentBlobGasUsed { get; set; }
public UInt256? ParentExcessBlobGas { get; set; }

public Withdrawal[]? Withdrawals { get; set; }
public ulong ParentTimestamp { get; set; }
public UInt256? ParentDifficulty { get; set; }
public Hash256? ParentUncleHash { get; set; }
public Hash256? ParentBeaconBlockRoot { get; set; }
public UInt256? ParentBaseFee { get; set; }
public long ParentGasUsed { get; set; }
public long ParentGasLimit { get; set; }
public Dictionary<string, Hash256> BlockHashes { get; set; } = [];
public Ommer[] Ommers { get; set; } = [];
public ulong StateChainId { get; set; } = MainnetSpecProvider.Instance.ChainId;
public GethTraceOptions GethTraceOptions { get; set; } = GethTraceOptions.Default;
public bool IsTraceEnabled { get; set; } = false;
}
10 changes: 10 additions & 0 deletions tools/Evm/Evm/t8n/JsonTypes/TransactionMetaData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

namespace Evm.t8n.JsonTypes;

public class TransactionMetaData
{
public bool? Protected { get; set; }
public byte[]? SecretKey { get; set; }
}
88 changes: 64 additions & 24 deletions tools/Evm/Evm/t8n/T8NCommandArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,91 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System.CommandLine.Parsing;
using Nethermind.Specs;

namespace Evm.t8n;

public class T8NCommandArguments
{
public string? InputAlloc { get; set; }
public string? InputEnv { get; set; }
public string? InputTxs { get; set; }
public string InputAlloc { get; set; } = "alloc.json";
public string InputEnv { get; set; } = "env.json";
public string InputTxs { get; set; } = "txs.json";

public string? OutputAlloc { get; set; }
public string? OutputResult { get; set; }
public string OutputAlloc { get; set; } = "alloc.json";
public string OutputResult { get; set; } = "result.json";
public string? OutputBody { get; set; }
public string? OutputBaseDir { get; set; }

public ulong? StateChainId { get; set; }
public string? StateFork { get; set; }
public string? StateReward { get; set; }
public ulong StateChainId { get; set; } = MainnetSpecProvider.Instance.ChainId;
public string StateFork { get; set; } = "GrayGlacier";
public string StateReward { get; set; } = "0";

public bool? Trace { get; set; }
public bool? TraceMemory { get; set; }
public bool? TraceNoStack { get; set; }
public bool? TraceReturnData { get; set; }
public bool Trace { get; set; }
public bool TraceMemory { get; set; }
public bool TraceNoStack { get; set; }
public bool TraceReturnData { get; set; }

public static T8NCommandArguments FromParseResult(ParseResult parseResult)
{
return new T8NCommandArguments
var arguments = new T8NCommandArguments
{
InputAlloc = parseResult.GetValueForOption(T8NCommandOptions.InputAllocOpt),
InputEnv = parseResult.GetValueForOption(T8NCommandOptions.InputEnvOpt),
InputTxs = parseResult.GetValueForOption(T8NCommandOptions.InputTxsOpt),

OutputAlloc = parseResult.GetValueForOption(T8NCommandOptions.OutputAllocOpt),
OutputResult = parseResult.GetValueForOption(T8NCommandOptions.OutputResultOpt),
OutputBody = parseResult.GetValueForOption(T8NCommandOptions.OutputBodyOpt),
OutputBaseDir = parseResult.GetValueForOption(T8NCommandOptions.OutputBaseDirOpt),

StateChainId = parseResult.GetValueForOption(T8NCommandOptions.StateChainIdOpt),
StateFork = parseResult.GetValueForOption(T8NCommandOptions.StateForkOpt),
StateReward = parseResult.GetValueForOption(T8NCommandOptions.StateRewardOpt),

Trace = parseResult.GetValueForOption(T8NCommandOptions.TraceOpt),
TraceMemory = parseResult.GetValueForOption(T8NCommandOptions.TraceMemoryOpt),
TraceNoStack = parseResult.GetValueForOption(T8NCommandOptions.TraceNoStackOpt),
TraceReturnData = parseResult.GetValueForOption(T8NCommandOptions.TraceReturnDataOpt)
};

var inputAlloc = parseResult.GetValueForOption(T8NCommandOptions.InputAllocOpt);
if (inputAlloc is not null)
{
arguments.InputAlloc = inputAlloc;
}

var inputEnv = parseResult.GetValueForOption(T8NCommandOptions.InputEnvOpt);
if (inputEnv is not null)
{
arguments.InputEnv = inputEnv;
}

var inputTxs = parseResult.GetValueForOption(T8NCommandOptions.InputTxsOpt);
if (inputTxs is not null)
{
arguments.InputTxs = inputTxs;
}

var outputAlloc = parseResult.GetValueForOption(T8NCommandOptions.OutputAllocOpt);
if (outputAlloc is not null)
{
arguments.OutputAlloc = outputAlloc;
}

var outputResult = parseResult.GetValueForOption(T8NCommandOptions.OutputResultOpt);
if (outputResult is not null)
{
arguments.OutputResult = outputResult;
}

var stateFork = parseResult.GetValueForOption(T8NCommandOptions.StateForkOpt);
if (stateFork is not null)
{
arguments.StateFork = stateFork;
}

var stateReward = parseResult.GetValueForOption(T8NCommandOptions.StateRewardOpt);
if (stateReward is not null)
{
arguments.StateReward = stateReward;
}

var stateChainId = parseResult.GetValueForOption(T8NCommandOptions.StateChainIdOpt);
if (stateChainId.HasValue)
{
arguments.StateChainId = stateChainId.Value;
}

return arguments;
}

}
28 changes: 14 additions & 14 deletions tools/Evm/Evm/t8n/T8NCommandOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ namespace Evm.t8n;

public static class T8NCommandOptions
{
public static Option<string> InputAllocOpt { get; } = new Option<string>("--input.alloc", description: "Input allocations", getDefaultValue: () => "alloc.json");
public static Option<string> InputEnvOpt { get; } = new Option<string>("--input.env", description: "Input environment", getDefaultValue: () => "env.json");
public static Option<string> InputTxsOpt { get; } = new Option<string>("--input.txs", description: "Input transactions", getDefaultValue: () => "txs.json");
public static Option<string> InputAllocOpt { get; } = new("--input.alloc", description: "Input allocations");
public static Option<string> InputEnvOpt { get; } = new("--input.env", description: "Input environment");
public static Option<string> InputTxsOpt { get; } = new("--input.txs", description: "Input transactions");

public static Option<string> OutputAllocOpt { get; } = new Option<string>("--output.alloc", description: "Output allocations", getDefaultValue: () => "alloc.json");
public static Option<string> OutputResultOpt { get; } = new Option<string>("--output.result", description: "Output result", getDefaultValue: () => "result.json");
public static Option<string> OutputBodyOpt { get; } = new Option<string>("--output.body", description: "Output body");
public static Option<string> OutputBaseDirOpt { get; } = new Option<string>("--output.basedir", description: "Output base directory");
public static Option<string> OutputAllocOpt { get; } = new("--output.alloc", description: "Output allocations");
public static Option<string> OutputResultOpt { get; } = new("--output.result", description: "Output result");
public static Option<string> OutputBodyOpt { get; } = new("--output.body", description: "Output body");
public static Option<string> OutputBaseDirOpt { get; } = new("--output.basedir", description: "Output base directory");

public static Option<ulong> StateChainIdOpt { get; } = new Option<ulong>("--state.chainid", description: "State chain id", getDefaultValue: () => 1);
public static Option<string> StateForkOpt { get; } = new Option<string>("--state.fork", description: "State fork", getDefaultValue: () => "GrayGlacier");
public static Option<string> StateRewardOpt { get; } = new Option<string>("--state.reward", description: "State reward");
public static Option<ulong?> StateChainIdOpt { get; } = new("--state.chainid", description: "State chain id");
public static Option<string> StateForkOpt { get; } = new("--state.fork", description: "State fork");
public static Option<string> StateRewardOpt { get; } = new("--state.reward", description: "State reward");

public static Option<bool> TraceOpt { get; } = new Option<bool>("--trace", description: "Configures the use of the JSON opcode tracer. This tracer emits traces to files as trace-<txIndex>-<txHash>.json", getDefaultValue: () => false);
public static Option<bool> TraceMemoryOpt { get; } = new Option<bool>("--trace.memory", description: "Trace memory", getDefaultValue: () => false);
public static Option<bool> TraceNoStackOpt { get; } = new Option<bool>("--trace.nostack", description: "Trace no stack", getDefaultValue: () => false);
public static Option<bool> TraceReturnDataOpt { get; } = new Option<bool>("--trace.returndata", description: "Trace return data", getDefaultValue: () => false);
public static Option<bool> TraceOpt { get; } = new("--trace", description: "Configures the use of the JSON opcode tracer. This tracer emits traces to files as trace-<txIndex>-<txHash>.json");
public static Option<bool> TraceMemoryOpt { get; } = new("--trace.memory", description: "Trace memory");
public static Option<bool> TraceNoStackOpt { get; } = new("--trace.nostack", description: "Trace no stack");
public static Option<bool> TraceReturnDataOpt { get; } = new("--trace.returndata", description: "Trace return data");

public static Command CreateCommand()
{
Expand Down
Loading

0 comments on commit 65055f1

Please sign in to comment.