diff --git a/tools/Evm/Evm/t8n/JsonTypes/EnvJson.cs b/tools/Evm/Evm/t8n/JsonTypes/EnvJson.cs index 822684766d7..682ea8fe6bc 100644 --- a/tools/Evm/Evm/t8n/JsonTypes/EnvJson.cs +++ b/tools/Evm/Evm/t8n/JsonTypes/EnvJson.cs @@ -15,7 +15,7 @@ public class EnvJson public ulong CurrentTimestamp { get; set; } public Withdrawal[] Withdrawals { get; set; } = []; - private UInt256? CurrentRandom { get; set; } + public string? CurrentRandom { get; set; } public ulong ParentTimestamp { get; set; } public UInt256? ParentDifficulty { get; set; } public UInt256? CurrentBaseFee { get; set; } @@ -32,12 +32,13 @@ public class EnvJson public Dictionary BlockHashes { get; set; } = []; public Ommer[] Ommers { get; set; } = []; - public Hash256? GetCurrentRandom() + public Hash256? GetCurrentRandomHash256() { - if (CurrentRandom == null) return null; + // if (CurrentRandom == null) + return null; - Span bytes = stackalloc byte[32]; - CurrentRandom?.ToBigEndian(bytes); - return new Hash256(bytes); + // Span bytes = stackalloc byte[32]; + // CurrentRandom?.ToBigEndian(bytes); + // return new Hash256(bytes); } } diff --git a/tools/Evm/Evm/t8n/T8NExecutor.cs b/tools/Evm/Evm/t8n/T8NExecutor.cs index 3ee709d5ffa..c05cf4124d5 100644 --- a/tools/Evm/Evm/t8n/T8NExecutor.cs +++ b/tools/Evm/Evm/t8n/T8NExecutor.cs @@ -8,7 +8,5 @@ public static class T8NExecutor public static void Execute(T8NCommandArguments arguments) { var t8nTest = T8NInputProcessor.Process(arguments); - - Console.Write(t8nTest); } } diff --git a/tools/Evm/Evm/t8n/T8NInputProcessor.cs b/tools/Evm/Evm/t8n/T8NInputProcessor.cs index 3f40a81dc4c..38b98309f19 100644 --- a/tools/Evm/Evm/t8n/T8NInputProcessor.cs +++ b/tools/Evm/Evm/t8n/T8NInputProcessor.cs @@ -27,7 +27,9 @@ public static T8NTest Process(T8NCommandArguments arguments) throw new T8NException("Env is not provided", T8NErrorCodes.ErrorIO); } - (ISpecProvider specProvider, IReleaseSpec spec) = GetSpec(arguments); + (ISpecProvider specProvider, IReleaseSpec spec) = GetSpec(arguments, inputData.Env); + + T8NValidator.ApplyChecks(inputData.Env, specProvider, spec); var gethTraceOptions = new GethTraceOptions { @@ -44,7 +46,7 @@ public static T8NTest Process(T8NCommandArguments arguments) CurrentTimestamp = inputData.Env.CurrentTimestamp, CurrentNumber = inputData.Env.CurrentNumber, Withdrawals = inputData.Env.Withdrawals, - CurrentRandom = inputData.Env.GetCurrentRandom(), + CurrentRandom = inputData.Env.GetCurrentRandomHash256(), ParentTimestamp = inputData.Env.ParentTimestamp, ParentDifficulty = inputData.Env.ParentDifficulty, CurrentBaseFee = inputData.Env.CurrentBaseFee, @@ -61,12 +63,13 @@ public static T8NTest Process(T8NCommandArguments arguments) BlockHashes = inputData.Env.BlockHashes, StateChainId = arguments.StateChainId, GethTraceOptions = gethTraceOptions, + IsTraceEnabled = arguments.Trace, }; return test; } - private static (ISpecProvider, IReleaseSpec) GetSpec(T8NCommandArguments arguments) + private static (ISpecProvider, IReleaseSpec) GetSpec(T8NCommandArguments arguments, EnvJson env) { IReleaseSpec spec; try @@ -88,6 +91,11 @@ private static (ISpecProvider, IReleaseSpec) GetSpec(T8NCommandArguments argumen : new CustomSpecProvider(((ForkActivation)0, Frontier.Instance), ((ForkActivation)1, overridableReleaseSpec)); + if (spec is Paris) + { + specProvider.UpdateMergeTransitionInfo(env.CurrentNumber, 0); + } + return (specProvider, overridableReleaseSpec); } } diff --git a/tools/Evm/Evm/t8n/T8NInputReader.cs b/tools/Evm/Evm/t8n/T8NInputReader.cs index 021ccf5bb2f..1b7897c9288 100644 --- a/tools/Evm/Evm/t8n/T8NInputReader.cs +++ b/tools/Evm/Evm/t8n/T8NInputReader.cs @@ -32,7 +32,7 @@ public static InputData ReadInputData(T8NCommandArguments arguments) if (arguments.InputEnv != Stdin) { - inputData.Env = LoadDataFromFile(arguments.InputAlloc, "env"); + inputData.Env = LoadDataFromFile(arguments.InputEnv, "env"); } if (arguments.InputTxs != Stdin) diff --git a/tools/Evm/Evm/t8n/T8NValidator.cs b/tools/Evm/Evm/t8n/T8NValidator.cs index 5181e0c9cc5..7e5bc8aa70d 100644 --- a/tools/Evm/Evm/t8n/T8NValidator.cs +++ b/tools/Evm/Evm/t8n/T8NValidator.cs @@ -1,9 +1,105 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Evm.t8n.Errors; +using Evm.t8n.JsonTypes; +using Nethermind.Consensus.Ethash; +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Specs.Forks; + namespace Evm.t8n; -public class T8NValidator +public static class T8NValidator { - + public static void ApplyChecks(EnvJson env, ISpecProvider specProvider, IReleaseSpec spec) + { + ApplyLondonChecks(env, spec); + ApplyShanghaiChecks(env, spec); + ApplyCancunChecks(env, spec); + ApplyMergeChecks(env, specProvider); + } + + private static void ApplyLondonChecks(EnvJson env, IReleaseSpec spec) + { + if (spec is not London) return; + if (env.CurrentBaseFee != null) return; + + if (!env.ParentBaseFee.HasValue || env.CurrentNumber == 0) + { + throw new T8NException("EIP-1559 config but missing 'parentBaseFee' in env section", + T8NErrorCodes.ErrorConfig); + } + + var parent = Build.A.BlockHeader.WithNumber(env.CurrentNumber - 1).WithBaseFee(env.ParentBaseFee.Value) + .WithGasUsed(env.ParentGasUsed).WithGasLimit(env.ParentGasLimit).TestObject; + env.CurrentBaseFee = BaseFeeCalculator.Calculate(parent, spec); + } + + private static void ApplyShanghaiChecks(EnvJson env, IReleaseSpec spec) + { + if (spec is not Shanghai) return; + if (env.Withdrawals == null) + { + throw new T8NException("Shanghai config but missing 'withdrawals' in env section", + T8NErrorCodes.ErrorConfig); + } + } + + private static void ApplyCancunChecks(EnvJson env, IReleaseSpec spec) + { + if (spec is not Cancun) + { + env.ParentBeaconBlockRoot = null; + return; + } + + if (env.ParentBeaconBlockRoot == null) + { + throw new T8NException("post-cancun env requires parentBeaconBlockRoot to be set", + T8NErrorCodes.ErrorConfig); + } + } + + private static void ApplyMergeChecks(EnvJson env, ISpecProvider specProvider) + { + if (specProvider.TerminalTotalDifficulty?.IsZero ?? false) + { + if (env.CurrentRandom == null) + throw new T8NException("post-merge requires currentRandom to be defined in env", + T8NErrorCodes.ErrorConfig); + if (env.CurrentDifficulty?.IsZero ?? false) + throw new T8NException("post-merge difficulty must be zero (or omitted) in env", + T8NErrorCodes.ErrorConfig); + return; + } + + if (env.CurrentDifficulty != null) return; + if (!env.ParentDifficulty.HasValue) + { + throw new T8NException( + "currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty", + T8NErrorCodes.ErrorConfig); + } + + if (env.CurrentNumber == 0) + { + throw new T8NException("currentDifficulty needs to be provided for block number 0", + T8NErrorCodes.ErrorConfig); + } + + if (env.CurrentTimestamp <= env.ParentTimestamp) + { + throw new T8NException( + $"currentDifficulty cannot be calculated -- currentTime ({env.CurrentTimestamp}) needs to be after parent time ({env.ParentTimestamp})", + T8NErrorCodes.ErrorConfig); + } + + EthashDifficultyCalculator difficultyCalculator = new(specProvider); + + env.CurrentDifficulty = difficultyCalculator.Calculate(env.ParentDifficulty.Value, env.ParentTimestamp, + env.CurrentTimestamp, env.CurrentNumber, env.ParentUncleHash is not null); + } } +