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 input processing #7690

Merged
merged 9 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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