diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs deleted file mode 100644 index 874f6a40f6bb..000000000000 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; -using Ethereum.Test.Base; -using NUnit.Framework; - -namespace Ethereum.Blockchain.Pyspec.Test; - -[TestFixture] -[Parallelizable(ParallelScope.All)] -public class OsakaEofTests : EofTestBase -{ - [TestCaseSource(nameof(LoadTests))] - public void Test(EofTest test) => RunCITest(test); - - private static IEnumerable LoadTests() - { - return []; - // EOF tests - // - //TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() - //{ - // ArchiveName = "fixtures_eip7692.tar.gz", - // ArchiveVersion = "eip7692@v2.3.0" - //}, $"fixtures/eof_tests/osaka"); - //return loader.LoadTests().Select(t => new TestCaseData(t) - // .SetName(t.Name) - // .SetCategory(t.Category)); - } -} diff --git a/src/Nethermind/Ethereum.Test.Base/EofTest.cs b/src/Nethermind/Ethereum.Test.Base/EofTest.cs deleted file mode 100644 index 7bc817525f3e..000000000000 --- a/src/Nethermind/Ethereum.Test.Base/EofTest.cs +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Evm.EvmObjectFormat; - -namespace Ethereum.Test.Base; - -public class Result -{ - public string Fork { get; set; } - public bool Success { get; set; } - public string? Error { get; set; } -} - -public class VectorTest -{ - public byte[] Code { get; set; } - public ValidationStrategy ContainerKind { get; set; } -} - -public class EofTest : EthereumTest -{ - public VectorTest Vector { get; set; } - public Result Result { get; internal set; } - public string? Description { get; set; } - public string? Url { get; set; } - public string? Spec { get; set; } -} diff --git a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs deleted file mode 100644 index ccf153abd1f8..000000000000 --- a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using Nethermind.Core.Specs; -using Nethermind.Evm; -using Nethermind.Evm.Tracing; -using Nethermind.Logging; -using NUnit.Framework; -using Nethermind.Evm.EvmObjectFormat; - -namespace Ethereum.Test.Base -{ - public abstract class EofTestBase - { - private static ILogManager _logManager = new TestLogManager(LogLevel.Warn); - private static ILogger _logger = _logManager.GetClassLogger(); - - [SetUp] - public void Setup() - { - EofValidator.Logger = _logger; - } - - protected static void Setup(ILogManager logManager) - { - _logManager = logManager ?? LimboLogs.Instance; - _logger = _logManager.GetClassLogger(); - } - - protected void RunCITest(EofTest test) - { - var result = RunTest(test, NullTxTracer.Instance); - - if (result != test.Result.Success) - { - _logger.Info($"Spec: {test.Spec}"); - _logger.Info(test.Description); - _logger.Info($"Url: {test.Url}"); - } - - Assert.That(result, Is.EqualTo(test.Result.Success)); - } - - protected bool RunTest(EofTest test) => RunTest(test, NullTxTracer.Instance) == test.Result.Success; - - protected bool RunTest(EofTest test, ITxTracer txTracer) - { - _logger.Info($"Running {test.Name} at {DateTime.UtcNow:HH:mm:ss.ffffff}"); - Assert.That(test.LoadFailure, Is.Null, "test data loading failure"); - - VectorTest vector = test.Vector; - byte[] code = vector.Code; - ValidationStrategy strategy = vector.ContainerKind; - IReleaseSpec fork = test.Result.Fork switch - { - "Osaka" => Nethermind.Specs.Forks.Osaka.Instance, - "Prague" => Nethermind.Specs.Forks.Prague.Instance, - "Berlin" => Nethermind.Specs.Forks.Berlin.Instance, - "London" => Nethermind.Specs.Forks.London.Instance, - "Shanghai" => Nethermind.Specs.Forks.Shanghai.Instance, - "Constantinople" => Nethermind.Specs.Forks.Constantinople.Instance, - "Byzantium" => Nethermind.Specs.Forks.Byzantium.Instance, - "SpuriousDragon" => Nethermind.Specs.Forks.SpuriousDragon.Instance, - "TangerineWhistle" => Nethermind.Specs.Forks.TangerineWhistle.Instance, - "Homestead" => Nethermind.Specs.Forks.Homestead.Instance, - "Frontier" => Nethermind.Specs.Forks.Frontier.Instance, - _ => throw new NotSupportedException($"Fork {test.Result.Fork} is not supported") - }; - - return CodeDepositHandler.IsValidWithEofRules(fork, code, 1, strategy); - } - } -} diff --git a/src/Nethermind/Ethereum.Test.Base/EofTestJson.cs b/src/Nethermind/Ethereum.Test.Base/EofTestJson.cs deleted file mode 100644 index 0e2a4a6077c5..000000000000 --- a/src/Nethermind/Ethereum.Test.Base/EofTestJson.cs +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Ethereum.Test.Base -{ - public class EofTestJson - { - [JsonPropertyName("_info")] - public GeneralStateTestInfoJson? Info { get; set; } - - public Dictionary Vectors { get; set; } - - } -} diff --git a/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs b/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs index 339781597a72..485fe06a1c7f 100644 --- a/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs +++ b/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs @@ -30,7 +30,6 @@ public IEnumerable LoadTests(TestType testType) return testType switch { - TestType.Eof => JsonToEthereumTest.ConvertToEofTests(json), TestType.State => JsonToEthereumTest.ConvertStateTest(json), _ => JsonToEthereumTest.ConvertToBlockchainTests(json) }; diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs index f62272c575b5..185d2e53aac0 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs @@ -13,7 +13,6 @@ using Nethermind.Core.Test.Modules; using Nethermind.Crypto; using Nethermind.Evm; -using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.State; using Nethermind.Evm.Tracing; using Nethermind.Evm.TransactionProcessing; @@ -66,8 +65,6 @@ protected EthereumTestResult RunTest(GeneralStateTest test, ITxTracer txTracer) Assert.That(test.LoadFailure, Is.Null, "test data loading failure"); Assert.That(test.Transaction, Is.Not.Null, "there is no transaction in the test"); - EofValidator.Logger = _logger; - test.Fork = ChainUtils.ResolveSpec(test.Fork, test.ChainId); ISpecProvider specProvider = diff --git a/src/Nethermind/Ethereum.Test.Base/IEofTestRunner.cs b/src/Nethermind/Ethereum.Test.Base/IEofTestRunner.cs deleted file mode 100644 index dbe0c0fa4b8d..000000000000 --- a/src/Nethermind/Ethereum.Test.Base/IEofTestRunner.cs +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; - -namespace Ethereum.Test.Base -{ - public interface IEofTestRunner - { - IEnumerable RunTests(); - } -} diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index c36954080ca1..b164e15b1490 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -13,7 +13,6 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Crypto; -using Nethermind.Evm.EvmObjectFormat; using Nethermind.Int256; using Nethermind.Merge.Plugin.Data; using Nethermind.Serialization.Json; @@ -337,66 +336,6 @@ public static BlockchainTest Convert(string name, string category, BlockchainTes private static readonly EthereumJsonSerializer _serializer = new(); - public static IEnumerable ConvertToEofTests(string json) - { - Dictionary testsInFile = _serializer.Deserialize>(json); - List tests = []; - foreach (KeyValuePair namedTest in testsInFile) - { - (string name, string category) = GetNameAndCategory(namedTest.Key); - GetTestMetaData(namedTest, out string? description, out string? url, out string? spec); - - foreach (KeyValuePair pair in namedTest.Value.Vectors) - { - VectorTestJson vectorJson = pair.Value; - VectorTest vector = new() - { - Code = Bytes.FromHexString(vectorJson.Code), - ContainerKind = ParseContainerKind(vectorJson.ContainerKind) - }; - - foreach (KeyValuePair result in vectorJson.Results) - { - EofTest test = new() - { - Name = $"{name}", - Category = $"{category} [{result.Key}]", - Url = url, - Description = description, - Spec = spec, - Vector = vector, - Result = result.ToTestResult() - }; - tests.Add(test); - } - } - } - - return tests; - - static ValidationStrategy ParseContainerKind(string containerKind) - => "INITCODE".Equals(containerKind) ? ValidationStrategy.ValidateInitCodeMode : ValidationStrategy.ValidateRuntimeMode; - - static void GetTestMetaData(KeyValuePair namedTest, out string? description, out string? url, out string? spec) - { - description = null; - url = null; - spec = null; - GeneralStateTestInfoJson info = namedTest.Value?.Info; - if (info is not null) - { - description = info.Description; - url = info.Url; - spec = info.Spec; - } - } - } - - private static Result ToTestResult(this KeyValuePair result) - => result.Value.Result ? - new Result { Fork = result.Key, Success = true } : - new Result { Fork = result.Key, Success = false, Error = result.Value.Exception }; - public static IEnumerable ConvertStateTest(string json) { Dictionary testsInFile = diff --git a/src/Nethermind/Ethereum.Test.Base/LoadEofTestsFileStrategy.cs b/src/Nethermind/Ethereum.Test.Base/LoadEofTestsFileStrategy.cs deleted file mode 100644 index e837021e6096..000000000000 --- a/src/Nethermind/Ethereum.Test.Base/LoadEofTestsFileStrategy.cs +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using System.IO; - -namespace Ethereum.Test.Base -{ - public class LoadEofTestFileStrategy : ITestLoadStrategy - { - public IEnumerable Load(string testName, string? wildcard = null) - { - //in case user wants to give a test file other than the ones in ethereum tests submodule - if (File.Exists(testName)) - { - FileTestsSource fileTestsSource = new(testName, wildcard); - IEnumerable tests = fileTestsSource.LoadTests(TestType.Eof); - - return tests; - } - - string testsDirectory = GetEofTestsDirectory(); - - IEnumerable testFiles = Directory.EnumerateFiles(testsDirectory, testName, SearchOption.AllDirectories); - - List eofTests = new(); - - //load all tests from found test files in ethereum tests submodule - foreach (string testFile in testFiles) - { - FileTestsSource fileTestsSource = new(testFile, wildcard); - IEnumerable tests = fileTestsSource.LoadTests(TestType.Eof); - - eofTests.AddRange(tests); - } - - return eofTests; - } - - private string GetEofTestsDirectory() - { - string currentDirectory = AppDomain.CurrentDomain.BaseDirectory; - - return Path.Combine(currentDirectory.Remove(currentDirectory.LastIndexOf("src", StringComparison.Ordinal)), "src", "tests", "EOFTests"); - } - } -} diff --git a/src/Nethermind/Ethereum.Test.Base/LoadEofTestsStrategy.cs b/src/Nethermind/Ethereum.Test.Base/LoadEofTestsStrategy.cs deleted file mode 100644 index ada757c11754..000000000000 --- a/src/Nethermind/Ethereum.Test.Base/LoadEofTestsStrategy.cs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.IO; - -namespace Ethereum.Test.Base; - -public class LoadEofTestsStrategy() - : TestLoadStrategy(Path.Combine("EIPTests", "StateTests", "stEOF"), TestType.State); diff --git a/src/Nethermind/Ethereum.Test.Base/TestType.cs b/src/Nethermind/Ethereum.Test.Base/TestType.cs index 8fa7ae90180b..937266252de1 100644 --- a/src/Nethermind/Ethereum.Test.Base/TestType.cs +++ b/src/Nethermind/Ethereum.Test.Base/TestType.cs @@ -6,6 +6,5 @@ namespace Ethereum.Test.Base; public enum TestType { Blockchain, - State, - Eof + State } diff --git a/src/Nethermind/EthereumTests.slnx b/src/Nethermind/EthereumTests.slnx index b5e21d12b74a..c75c6624bf48 100644 --- a/src/Nethermind/EthereumTests.slnx +++ b/src/Nethermind/EthereumTests.slnx @@ -64,7 +64,6 @@ - diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/AlwaysCancelTxTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/AlwaysCancelTxTracer.cs index 7d633f1ed7d4..6d61ad0d7a46 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/AlwaysCancelTxTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/AlwaysCancelTxTracer.cs @@ -50,7 +50,7 @@ public static AlwaysCancelTxTracer Instance public void MarkAsFailed(Address recipient, in GasConsumed gasSpent, byte[] output, string? error, Hash256? stateRoot = null) => throw new OperationCanceledException(ErrorMessage); - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) => throw new OperationCanceledException(ErrorMessage); + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) => throw new OperationCanceledException(ErrorMessage); public void ReportOperationError(EvmExceptionType error) => throw new OperationCanceledException(ErrorMessage); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockReceiptsTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockReceiptsTracer.cs index 308b23b20a05..bb7c675671ef 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockReceiptsTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockReceiptsTracer.cs @@ -125,8 +125,8 @@ protected virtual TxReceipt BuildReceipt(Address recipient, in GasConsumed gasCo return txReceipt; } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) => - _currentTxTracer.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) => + _currentTxTracer.StartOperation(pc, opcode, gas, env); public void ReportOperationError(EvmExceptionType error) => _currentTxTracer.ReportOperationError(error); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs index d2602f9dcb07..ffa04f78e969 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs @@ -11,7 +11,6 @@ using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.Evm.TransactionProcessing; -using Nethermind.Evm.CodeAnalysis; namespace Nethermind.Blockchain.Tracing.GethStyle.Custom.JavaScript; @@ -107,16 +106,14 @@ public override void ReportAction(long gas, UInt256 value, Address from, Address : new Log.Contract(from, to, value, isAnyCreate ? null : input); } - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { - _log.pc = pc + (env.CodeInfo is EofCodeInfo eof ? eof.PcOffset() : 0); + _log.pc = pc; _log.op = new Log.Opcode(opcode); _log.gas = gas; _log.depth = env.GetGethTraceDepth(); _log.error = null; _log.gasCost = null; - // skip codeSection - // skip functionDepth } public override void ReportOperationRemainingGas(long gas) diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/Log.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/Log.cs index 97851acb0e7a..3c3aee02cf9c 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/Log.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/Log.cs @@ -40,7 +40,7 @@ public readonly struct Opcode public Instruction Value { get; } public Opcode(Instruction value) => Value = value; public int toNumber() => (int)Value; - public string? toString() => Value.GetName(); + public string? toString() => Enum.GetName(Value); public bool isPush() => Value is >= Instruction.PUSH0 and <= Instruction.PUSH32; } diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/Call/NativeCallTracerCallFrameConverter.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/Call/NativeCallTracerCallFrameConverter.cs index 406fc76e45a7..0ee204dbfd65 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/Call/NativeCallTracerCallFrameConverter.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/Call/NativeCallTracerCallFrameConverter.cs @@ -20,7 +20,7 @@ public override void Write(Utf8JsonWriter writer, NativeCallTracerCallFrame valu ForcedNumberConversion.ForcedConversion.Value = NumberConversion.Hex; writer.WritePropertyName("type"u8); - JsonSerializer.Serialize(writer, value.Type.GetName(), options); + JsonSerializer.Serialize(writer, Enum.GetName(value.Type), options); writer.WritePropertyName("from"u8); JsonSerializer.Serialize(writer, value.From, options); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/FourByte/Native4ByteTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/FourByte/Native4ByteTracer.cs index db435c4a0643..74ad3ec93361 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/FourByte/Native4ByteTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/FourByte/Native4ByteTracer.cs @@ -64,7 +64,7 @@ public override void ReportAction(long gas, UInt256 value, Address from, Address } } - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { _op = opcode; } diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/Prestate/NativePrestateTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/Prestate/NativePrestateTracer.cs index 9b6a5a34ae8c..5d8625b51948 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/Prestate/NativePrestateTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/Native/Prestate/NativePrestateTracer.cs @@ -94,9 +94,9 @@ public override void MarkAsFailed(Address recipient, in GasConsumed gasSpent, by ProcessDiffState(); } - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { - base.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); + base.StartOperation(pc, opcode, gas, env); if (_error is not null) return; diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxMemoryTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxMemoryTracer.cs index a3699872c1da..a15c32b78208 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxMemoryTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxMemoryTracer.cs @@ -51,12 +51,12 @@ public override void SetOperationStorage(Address address, UInt256 storageIndex, .ToHexString(false); } - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { GethTxMemoryTraceEntry previousTraceEntry = CurrentTraceEntry; var previousDepth = CurrentTraceEntry?.Depth ?? 0; - base.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); + base.StartOperation(pc, opcode, gas, env); if (CurrentTraceEntry.Depth > previousDepth) { diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxTracer.cs index 3942cd3bfd9b..ebdb199254b4 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/GethLikeTxTracer.cs @@ -52,8 +52,6 @@ public override void MarkAsFailed(Address recipient, in GasConsumed gasSpent, by EvmExceptionType.StackOverflow => "StackOverflow", EvmExceptionType.StackUnderflow => "StackUnderflow", EvmExceptionType.OutOfGas => "OutOfGas", - EvmExceptionType.InvalidSubroutineEntry => "InvalidSubroutineEntry", - EvmExceptionType.InvalidSubroutineReturn => "InvalidSubroutineReturn", EvmExceptionType.InvalidJumpDestination => "BadJumpDestination", EvmExceptionType.AccessViolation => "AccessViolation", EvmExceptionType.StaticCallViolation => "StaticCallViolation", @@ -71,7 +69,7 @@ public override void MarkAsFailed(Address recipient, in GasConsumed gasSpent, by protected GethLikeTxTracer(GethTraceOptions options) : base(options) { } private bool _gasCostAlreadySetForCurrentOp; - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { if (CurrentTraceEntry is not null) { @@ -81,10 +79,8 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe CurrentTraceEntry = CreateTraceEntry(opcode); CurrentTraceEntry.Depth = env.GetGethTraceDepth(); CurrentTraceEntry.Gas = gas; - CurrentTraceEntry.Opcode = opcode.GetName(); + CurrentTraceEntry.Opcode = Enum.GetName(opcode); CurrentTraceEntry.ProgramCounter = pc; - // skip codeSection - // skip functionDepth _gasCostAlreadySetForCurrentOp = false; } diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/ParityStyle/ParityLikeTxTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/ParityStyle/ParityLikeTxTracer.cs index dac228af3030..89fe7801382e 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/ParityStyle/ParityLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/ParityStyle/ParityLikeTxTracer.cs @@ -9,7 +9,6 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Evm; -using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.Tracing; using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; @@ -80,8 +79,7 @@ private static string GetCallType(ExecutionType executionType) { return executionType switch { - ExecutionType.CREATE or ExecutionType.CREATE2 or ExecutionType.EOFCREATE - or ExecutionType.TXCREATE => "create", + ExecutionType.CREATE or ExecutionType.CREATE2 => "create", ExecutionType.CALL or ExecutionType.TRANSACTION => "call", ExecutionType.DELEGATECALL => "delegatecall", ExecutionType.STATICCALL => "staticcall", @@ -94,8 +92,7 @@ private static string GetActionType(ExecutionType executionType) { return executionType switch { - ExecutionType.CREATE or ExecutionType.CREATE2 or ExecutionType.EOFCREATE - or ExecutionType.TXCREATE => "create", + ExecutionType.CREATE or ExecutionType.CREATE2 => "create", _ => "call" }; } @@ -109,8 +106,6 @@ ExecutionType.CREATE or ExecutionType.CREATE2 or ExecutionType.EOFCREATE EvmExceptionType.StackOverflow => "Stack overflow", EvmExceptionType.StackUnderflow => "Stack underflow", EvmExceptionType.OutOfGas => "Out of gas", - EvmExceptionType.InvalidSubroutineEntry => "Invalid subroutine entry", - EvmExceptionType.InvalidSubroutineReturn => "Invalid subroutine return", EvmExceptionType.InvalidJumpDestination => "Bad jump destination", EvmExceptionType.AccessViolation => "Access violation", EvmExceptionType.StaticCallViolation => "Static call violation", @@ -232,15 +227,12 @@ public override void MarkAsFailed(Address recipient, in GasConsumed gasSpent, by }; } - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, - int codeSection = 0, int functionDepth = 0) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { ParityVmOperationTrace operationTrace = new(); _gasAlreadySetForCurrentOp = false; - operationTrace.Pc = pc + (env.CodeInfo is EofCodeInfo eof ? eof.PcOffset() : 0); + operationTrace.Pc = pc; operationTrace.Cost = gas; - // skip codeSection - // skip functionDepth _currentOperation = operationTrace; _currentPushList.Clear(); _currentVmTrace.Ops.Add(operationTrace); @@ -405,8 +397,6 @@ public override void ReportAction(long gas, UInt256 value, Address from, Address { ExecutionType.CREATE => "create", ExecutionType.CREATE2 => "create2", - ExecutionType.EOFCREATE => "create3", - ExecutionType.TXCREATE => "create4", _ => null }; } diff --git a/src/Nethermind/Nethermind.Core/GasCostOf.cs b/src/Nethermind/Nethermind.Core/GasCostOf.cs index 24311b8d7ef9..862232c33eb9 100644 --- a/src/Nethermind/Nethermind.Core/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Core/GasCostOf.cs @@ -89,22 +89,5 @@ public static class GasCostOf public const long MinModExpEip2565 = 200; // eip-2565 public const long MinModExpEip7883 = 500; // eip-7883 - // Eof Execution EIP-7692 - public const long DataLoad = 4; - public const long DataLoadN = 3; - public const long DataCopy = 3; - public const long DataSize = 2; - public const long ReturnCode = 0; - public const long EofCreate = 32000; - public const long ReturnDataLoad = 3; - public const long RJump = 2; - public const long RJumpi = 4; - public const long RJumpv = 4; - public const long Exchange = 3; - public const long Swapn = 3; - public const long Dupn = 3; - public const long Callf = 5; - public const long Jumpf = 5; - public const long Retf = 3; } } diff --git a/src/Nethermind/Nethermind.Core/Messages/TxErrorMessages.cs b/src/Nethermind/Nethermind.Core/Messages/TxErrorMessages.cs index f3da0ea2ef0a..076b4f50bade 100644 --- a/src/Nethermind/Nethermind.Core/Messages/TxErrorMessages.cs +++ b/src/Nethermind/Nethermind.Core/Messages/TxErrorMessages.cs @@ -82,18 +82,6 @@ public static string BlobTxGasLimitExceeded(ulong totalDataGas, ulong maxBlobGas public static string TxGasLimitCapExceeded(long gasLimit, long gasLimitCap) => $"TxGasLimitCapExceeded: Gas limit {gasLimit} exceeded cap of {gasLimitCap}."; - public const string InvalidCreateTxData - = "InvalidCreateTxData: Legacy createTx cannot create Eof code"; - - public const string TooManyEofInitcodes - = $"TooManyEofInitcodes: Eof initcodes count exceeded limit"; - - public const string EmptyEofInitcodesField - = $"EmptyEofInitcodesField: Eof initcodes count must be greater than 0"; - - public const string EofContractSizeInvalid - = "EofContractSizeInvalid: Eof initcode size is invalid (either 0 or too big)"; - public const string NonceTooHigh = "NonceTooHigh: Nonce exceeds max nonce"; } diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index 918ec77d0bd2..206246854660 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -316,12 +316,7 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec bool IsEip6780Enabled { get; } /// - /// Eof execution env in EVM - /// - bool IsEofEnabled { get; } - - /// - /// EIP-8024: Backward-compatible SWAPN, DUPN, EXCHANGE for legacy (non-EOF) code + /// EIP-8024: Backward-compatible SWAPN, DUPN, EXCHANGE /// bool IsEip8024Enabled { get; } diff --git a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs index 7079f7a763ca..da11649b17f5 100644 --- a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs +++ b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs @@ -79,7 +79,6 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec public virtual Address? Eip2935ContractAddress => spec.Eip2935ContractAddress; public virtual long Eip2935RingBufferSize => spec.Eip2935RingBufferSize; public virtual bool IsEip6780Enabled => spec.IsEip6780Enabled; - public virtual bool IsEofEnabled => spec.IsEofEnabled; public virtual bool IsEip7702Enabled => spec.IsEip7702Enabled; public virtual bool IsEip7823Enabled => spec.IsEip7823Enabled; public virtual bool IsEip7825Enabled => spec.IsEip7825Enabled; diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index cc205b6b20e6..b615aa782fce 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -21,7 +21,6 @@ namespace Nethermind.Core [DebuggerDisplay("{Hash}, Value: {Value}, To: {To}, Gas: {GasLimit}")] public class Transaction { - public static ReadOnlySpan EofMagic => [0xEF, 0x00]; public const byte MaxTxType = 0x7F; public const int BaseTxGasCost = 21000; @@ -75,8 +74,6 @@ public class Transaction public Signature? Signature { get; set; } public bool IsSigned => Signature is not null; public bool IsContractCreation => To is null; - public bool IsEofContractCreation => IsContractCreation && Data.Span.StartsWith(EofMagic); - public bool IsLegacyContractCreation => IsContractCreation && !IsEofContractCreation; public bool IsMessageCall => To is not null; [MemberNotNullWhen(true, nameof(AuthorizationList))] diff --git a/src/Nethermind/Nethermind.EofParse.Runner/Nethermind.EofParse.Runner.csproj b/src/Nethermind/Nethermind.EofParse.Runner/Nethermind.EofParse.Runner.csproj deleted file mode 100644 index a05665c133be..000000000000 --- a/src/Nethermind/Nethermind.EofParse.Runner/Nethermind.EofParse.Runner.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - netheofparse - annotations - true - true - false - false - true - true - - - - - - - - - - - - diff --git a/src/Nethermind/Nethermind.EofParse.Runner/Program.cs b/src/Nethermind/Nethermind.EofParse.Runner/Program.cs deleted file mode 100644 index ae2c7d4ada4a..000000000000 --- a/src/Nethermind/Nethermind.EofParse.Runner/Program.cs +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.CommandLine; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Nethermind.Core.Extensions; -using Nethermind.Evm.EvmObjectFormat; - -namespace Nethermind.EofParse.Runner; - -internal class Program -{ - public class Options - { - public static Option Input { get; } = - new("--input", "-i") { Description = "Set the raw eof test input file or directory. Either 'input' or 'stdin' is required." }; - - public static Option Stdin { get; } = - new("--stdin", "-x") { Description = "If stdin is used, the eof runner will read inputs (filenames) from stdin, and continue executing until empty line is read." }; - } - - public static async Task Main(params string[] args) - { - RootCommand rootCommand = - [ - Options.Input, - Options.Stdin - ]; - rootCommand.SetAction(Run); - - return await rootCommand.Parse(args).InvokeAsync(); - } - - private static Task Run(ParseResult parseResult, CancellationToken cancellationToken) - { - string input = parseResult.GetValue(Options.Input); - - if (parseResult.GetValue(Options.Stdin)) - input = Console.ReadLine(); - - while (!string.IsNullOrWhiteSpace(input)) - { - if (!input.StartsWith('#')) - { - input = new string(input.Where(c => char.IsLetterOrDigit(c)).ToArray()); - - var bytecode = Bytes.FromHexString(input); - try - { - var validationResult = EofValidator.IsValidEof(bytecode, ValidationStrategy.ValidateRuntimeMode, - out EofContainer? header); - if (validationResult) - { - var sectionCount = header.Value.CodeSections.Length; - var subContainerCount = header.Value.ContainerSections?.Length ?? 0; - var dataCount = header.Value.DataSection.Length; - Console.WriteLine($"OK {sectionCount}/{subContainerCount}/{dataCount}"); - } - else - { - Console.WriteLine($"err: unknown"); - } - } - catch (Exception e) - { - Console.WriteLine($"err: {e.Message}"); - } - - if (!parseResult.GetValue(Options.Stdin)) - break; - } - - input = Console.ReadLine(); - } - - return Task.CompletedTask; - } -} diff --git a/src/Nethermind/Nethermind.EofParse.Runner/Properties/launchSettings.json b/src/Nethermind/Nethermind.EofParse.Runner/Properties/launchSettings.json deleted file mode 100644 index e60316dc6525..000000000000 --- a/src/Nethermind/Nethermind.EofParse.Runner/Properties/launchSettings.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "Valid": { - "commandName": "Project", - "commandLineArgs": "-i ef00010100040200010006040000000080000260006000fe00" - }, - "Invalid": { - "commandName": "Project", - "commandLineArgs": "-i ef000101000402000100010400000000800000c0" - }, - "Console": { - "commandName": "Project", - "commandLineArgs": "-x" - }, - "no args": { - "commandName": "Project", - "commandLineArgs": "" - } - } -} diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmOpcodesBenchmark.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmOpcodesBenchmark.cs index 5e4031375bf8..c1984d8a58b3 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmOpcodesBenchmark.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmOpcodesBenchmark.cs @@ -87,7 +87,7 @@ public unsafe class EvmOpcodesBenchmark private static readonly byte[] StopCode = [(byte)Instruction.STOP]; private static readonly Instruction[] AllValidLegacyOpcodes = Enum .GetValues() - .Where(static opcode => opcode.IsValid(isEofContext: false) && opcode != Instruction.INVALID) + .Where(static opcode => Enum.IsDefined(opcode) && opcode != Instruction.INVALID) .ToArray(); private static readonly Instruction[] PerRunRefreshedOpcodes = [ @@ -414,13 +414,43 @@ private static int CalculateRunsPerBatch(Instruction opcode, int depth, int requ return Math.Clamp(maxRuns, 1, requestedRuns); } + private static (ushort InputCount, ushort OutputCount, ushort immediates) StackRequirements(Instruction instruction) => instruction switch + { + Instruction.STOP or Instruction.INVALID or Instruction.JUMPDEST => (0, 0, 0), + Instruction.POP or Instruction.SELFDESTRUCT or Instruction.JUMP => (1, 0, 0), + Instruction.ISZERO or Instruction.NOT or Instruction.CLZ or Instruction.BALANCE or Instruction.CALLDATALOAD + or Instruction.EXTCODESIZE or Instruction.EXTCODEHASH or Instruction.BLOCKHASH or Instruction.MLOAD + or Instruction.SLOAD or Instruction.BLOBHASH or Instruction.TLOAD => (1, 1, 0), + Instruction.MSTORE or Instruction.MSTORE8 or Instruction.SSTORE or Instruction.LOG0 or Instruction.REVERT + or Instruction.TSTORE or Instruction.RETURN or Instruction.JUMPI => (2, 0, 0), + Instruction.CALLDATACOPY or Instruction.CODECOPY or Instruction.RETURNDATACOPY or Instruction.LOG1 + or Instruction.MCOPY => (3, 0, 0), + Instruction.EXTCODECOPY or Instruction.LOG2 => (4, 0, 0), + Instruction.LOG3 => (5, 0, 0), + Instruction.LOG4 => (6, 0, 0), + Instruction.ADDMOD or Instruction.MULMOD or Instruction.CREATE => (3, 1, 0), + Instruction.CREATE2 => (4, 1, 0), + Instruction.ADDRESS or Instruction.ORIGIN or Instruction.CALLER or Instruction.CALLVALUE + or Instruction.CALLDATASIZE or Instruction.CODESIZE or Instruction.GASPRICE or Instruction.RETURNDATASIZE + or Instruction.COINBASE or Instruction.TIMESTAMP or Instruction.NUMBER or Instruction.PREVRANDAO + or Instruction.GASLIMIT or Instruction.CHAINID or Instruction.SELFBALANCE or Instruction.BASEFEE + or Instruction.MSIZE or Instruction.GAS or Instruction.PC or Instruction.BLOBBASEFEE + or Instruction.SLOTNUM => (0, 1, 0), + Instruction.CALL or Instruction.DELEGATECALL or Instruction.STATICCALL or Instruction.CALLCODE => (6, 1, 0), + >= Instruction.PUSH0 and <= Instruction.PUSH32 => (0, 1, (ushort)(instruction - Instruction.PUSH0)), + >= Instruction.DUP1 and <= Instruction.DUP16 => ((ushort)(instruction - Instruction.DUP1 + 1), (ushort)(instruction - Instruction.DUP1 + 2), 0), + >= Instruction.SWAP1 and <= Instruction.SWAP16 => ((ushort)(instruction - Instruction.SWAP1 + 2), (ushort)(instruction - Instruction.SWAP1 + 2), 0), + Instruction.SWAPN or Instruction.DUPN or Instruction.EXCHANGE => (0, 0, 1), + _ => Enum.IsDefined(instruction) ? ((ushort)2, (ushort)1, (ushort)0) : throw new NotImplementedException($"opcode {instruction} not implemented yet"), + }; + private static (int InputCount, int OutputCount) GetStackIo(Instruction opcode) { return opcode switch { Instruction.CALL or Instruction.CALLCODE => (7, 1), Instruction.DELEGATECALL or Instruction.STATICCALL => (6, 1), - _ => (opcode.StackRequirements().InputCount, opcode.StackRequirements().OutputCount), + _ => (StackRequirements(opcode).InputCount, StackRequirements(opcode).OutputCount), }; } @@ -556,7 +586,7 @@ private int SetupExtCodeCopyStack(int runs) private int SetupGenericStack(Instruction opcode) { - int inputCount = opcode.StackRequirements().InputCount; + int inputCount = StackRequirements(opcode).InputCount; for (int i = 0; i < inputCount; i++) { UInt256 value = new((ulong)(i + 1)); diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index bb15e0804cc9..167f8877da76 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -407,7 +407,7 @@ public void MarkAsFailed(Address recipient, in GasConsumed gasSpent, byte[] outp { } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { } diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 67cb80d92be2..53d260aa166b 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -103,24 +103,18 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase CancunInstructions.Union( new Instruction[] { - Instruction.RJUMP, - Instruction.RJUMPI, - Instruction.RJUMPV, - Instruction.CALLF, - Instruction.RETF, - Instruction.JUMPF, - Instruction.EOFCREATE, - Instruction.RETURNCODE, - Instruction.DATASIZE, - Instruction.DATACOPY, - Instruction.DATALOAD, - Instruction.DATALOADN, + Instruction.CLZ, + } + ).ToArray(); + + private static readonly Instruction[] AmsterdamInstructions = + OsakaInstructions.Union( + new Instruction[] + { + Instruction.SLOTNUM, Instruction.SWAPN, Instruction.DUPN, Instruction.EXCHANGE, - Instruction.EXTCALL, - Instruction.EXTDELEGATECALL, - Instruction.EXTSTATICCALL, } ).ToArray(); @@ -141,7 +135,8 @@ private readonly Dictionary _validOpcodes {MainnetSpecProvider.CancunActivation, CancunInstructions}, {MainnetSpecProvider.PragueActivation, CancunInstructions}, {MainnetSpecProvider.OsakaActivation, OsakaInstructions}, - {(long.MaxValue, ulong.MaxValue), OsakaInstructions} + {MainnetSpecProvider.AmsterdamActivation, AmsterdamInstructions}, + {(long.MaxValue, ulong.MaxValue), AmsterdamInstructions} }; private const string InvalidOpCodeErrorMessage = "BadInstruction"; @@ -167,6 +162,8 @@ protected override ILogManager GetLogManager() [TestCase(MainnetSpecProvider.ParisBlockNumber + 1, MainnetSpecProvider.ShanghaiBlockTimestamp)] [TestCase(MainnetSpecProvider.ParisBlockNumber + 2, MainnetSpecProvider.CancunBlockTimestamp)] [TestCase(MainnetSpecProvider.ParisBlockNumber + 3, MainnetSpecProvider.PragueBlockTimestamp)] + [TestCase(MainnetSpecProvider.ParisBlockNumber + 4, MainnetSpecProvider.OsakaBlockTimestamp)] + [TestCase(MainnetSpecProvider.ParisBlockNumber + 10, MainnetSpecProvider.AmsterdamBlockTimestamp)] public void Test(long blockNumber, ulong? timestamp = null) { ILogger logger = _logManager.GetClassLogger(); diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionSubstateTests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionSubstateTests.cs index 16a9e6153355..d57751d3c797 100644 --- a/src/Nethermind/Nethermind.Evm.Test/TransactionSubstateTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionSubstateTests.cs @@ -28,7 +28,7 @@ public void should_return_proper_revert_error_when_there_is_no_exception() 0x05, 0x06, 0x07, 0x08, 0x09 }; ReadOnlyMemory readOnlyMemory = new(data); - TransactionSubstate transactionSubstate = new((CodeInfo.Empty, readOnlyMemory), + TransactionSubstate transactionSubstate = new(readOnlyMemory, 0, new JournalSet
(Address.EqualityComparer), new JournalCollection(), @@ -42,7 +42,7 @@ public void should_return_proper_revert_error_when_there_is_exception() { byte[] data = { 0x05, 0x06, 0x07, 0x08, 0x09 }; ReadOnlyMemory readOnlyMemory = new(data); - TransactionSubstate transactionSubstate = new((CodeInfo.Empty, readOnlyMemory), + TransactionSubstate transactionSubstate = new(readOnlyMemory, 0, new JournalSet
(Address.EqualityComparer), new JournalCollection(), @@ -56,7 +56,7 @@ public void should_return_weird_revert_error_when_there_is_exception() { byte[] data = TransactionSubstate.ErrorFunctionSelector.Concat(Bytes.FromHexString("0x00000001000000000000000000000000000000000000000012a9d65e7d180cfcf3601b6d00000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000006a000000000300000000000115859c410282f6600012efb47fcfcad4f96c83d4ca676842fb03ef20a4770000000015f762bdaa80f6d9dc5518ff64cb7ba5717a10dabc4be3a41acd2c2f95ee22000012a9d65e7d180cfcf3601b6df0000000000000185594dac7eb0828ff000000000000000000000000")).ToArray(); ReadOnlyMemory readOnlyMemory = new(data); - TransactionSubstate transactionSubstate = new((CodeInfo.Empty, readOnlyMemory), + TransactionSubstate transactionSubstate = new(readOnlyMemory, 0, new JournalSet
(Address.EqualityComparer), new JournalCollection(), @@ -76,7 +76,7 @@ public void should_return_proper_revert_error_when_revert_custom_error_badly_imp byte[] data = Bytes.FromHexString(hex); ReadOnlyMemory readOnlyMemory = new(data); TransactionSubstate transactionSubstate = new( - (CodeInfo.Empty, readOnlyMemory), + readOnlyMemory, 0, new JournalSet
(Address.EqualityComparer), new JournalCollection(), @@ -149,7 +149,7 @@ public void should_return_proper_revert_error_when_using_special_functions((byte // See: https://docs.soliditylang.org/en/latest/control-structures.html#revert ReadOnlyMemory readOnlyMemory = new(tc.data); TransactionSubstate transactionSubstate = new( - (CodeInfo.Empty, readOnlyMemory), + readOnlyMemory, 0, new JournalSet
(Address.EqualityComparer), new JournalCollection(), @@ -173,7 +173,7 @@ public void should_return_proper_revert_error_when_revert_custom_error() }; ReadOnlyMemory readOnlyMemory = new(data); TransactionSubstate transactionSubstate = new( - (CodeInfo.Empty, readOnlyMemory), + readOnlyMemory, 0, new JournalSet
(Address.EqualityComparer), new JournalCollection(), diff --git a/src/Nethermind/Nethermind.Evm/CacheCodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CacheCodeInfoRepository.cs index 2bfcc06aa31d..6067b6784449 100644 --- a/src/Nethermind/Nethermind.Evm/CacheCodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CacheCodeInfoRepository.cs @@ -25,7 +25,7 @@ public CacheCodeInfoRepository(IWorldState worldState, IPrecompileProvider preco _inner = new CodeInfoRepository(worldState, precompileProvider, GetOrCacheCodeInfo); } - private CodeInfo GetOrCacheCodeInfo(ValueHash256 codeHash, IReleaseSpec spec) + private CodeInfo GetOrCacheCodeInfo(ValueHash256 codeHash) { if (codeHash == ValueKeccak.OfAnEmptyString) { @@ -35,7 +35,7 @@ private CodeInfo GetOrCacheCodeInfo(ValueHash256 codeHash, IReleaseSpec spec) CodeInfo? cachedCodeInfo = _codeCache.Get(in codeHash); if (cachedCodeInfo is null) { - cachedCodeInfo = CodeInfoRepository.GetCodeInfo(_worldState, in codeHash, spec); + cachedCodeInfo = CodeInfoRepository.GetCodeInfo(_worldState, in codeHash); _codeCache.Set(in codeHash, cachedCodeInfo); } else @@ -59,7 +59,7 @@ public void InsertCode(ReadOnlyMemory code, Address codeOwner, IReleaseSpe { if (CodeInfoRepository.InsertCode(_worldState, code, codeOwner, spec, out ValueHash256 codeHash) && _codeCache.Get(in codeHash) is null) { - _codeCache.Set(in codeHash, CodeInfoFactory.CreateCodeInfo(code, spec)); + _codeCache.Set(in codeHash, CodeInfoFactory.CreateCodeInfo(code)); } } diff --git a/src/Nethermind/Nethermind.Evm/CallResult.cs b/src/Nethermind/Nethermind.Evm/CallResult.cs index d23ac7ab54e3..c97fcf43601a 100644 --- a/src/Nethermind/Nethermind.Evm/CallResult.cs +++ b/src/Nethermind/Nethermind.Evm/CallResult.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.GasPolicy; namespace Nethermind.Evm; @@ -12,66 +11,43 @@ public partial class VirtualMachine { protected readonly ref struct CallResult { - public static CallResult InvalidSubroutineEntry => new(EvmExceptionType.InvalidSubroutineEntry); - public static CallResult InvalidSubroutineReturn => new(EvmExceptionType.InvalidSubroutineReturn); - public static CallResult OutOfGasException => new(EvmExceptionType.OutOfGas); - public static CallResult AccessViolationException => new(EvmExceptionType.AccessViolation); - public static CallResult InvalidJumpDestination => new(EvmExceptionType.InvalidJumpDestination); - public static CallResult InvalidInstructionException => new(EvmExceptionType.BadInstruction); - public static CallResult StaticCallViolationException => new(EvmExceptionType.StaticCallViolation); - public static CallResult StackOverflowException => new(EvmExceptionType.StackOverflow); - public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); - public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); - public static CallResult InvalidAddressRange => new(EvmExceptionType.AddressOutOfRange); - public static CallResult Empty(int fromVersion) => new(container: null, output: default, precompileSuccess: null, fromVersion); + public static CallResult Empty() => new(output: default, precompileSuccess: null); public CallResult(VmState stateToExecute) { StateToExecute = stateToExecute; - Output = (null, Array.Empty()); + Output = Array.Empty(); PrecompileSuccess = null; ShouldRevert = false; ExceptionType = EvmExceptionType.None; } - public CallResult(ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + public CallResult(ReadOnlyMemory output, bool? precompileSuccess, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) { StateToExecute = null; - Output = (null, output); + Output = output; PrecompileSuccess = precompileSuccess; ShouldRevert = shouldRevert; ExceptionType = exceptionType; - FromVersion = fromVersion; } - public CallResult(CodeInfo? container, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + internal CallResult(EvmExceptionType exceptionType) { StateToExecute = null; - Output = (container, output); - PrecompileSuccess = precompileSuccess; - ShouldRevert = shouldRevert; - ExceptionType = exceptionType; - FromVersion = fromVersion; - } - - private CallResult(EvmExceptionType exceptionType) - { - StateToExecute = null; - Output = (null, StatusCode.FailureBytes); + Output = StatusCode.FailureBytes; PrecompileSuccess = null; ShouldRevert = false; ExceptionType = exceptionType; } public VmState? StateToExecute { get; } - public (CodeInfo Container, ReadOnlyMemory Bytes) Output { get; } + public ReadOnlyMemory Output { get; } public EvmExceptionType ExceptionType { get; } public bool ShouldRevert { get; } public bool? PrecompileSuccess { get; } public bool IsReturn => StateToExecute is null; //EvmExceptionType.Revert is returned when the top frame encounters a REVERT opcode, which is not an exception. public bool IsException => ExceptionType != EvmExceptionType.None && ExceptionType != EvmExceptionType.Revert; - public int FromVersion { get; } public string? SubstateError { get; init; } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index b64cedd20251..808fe33df032 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -20,22 +20,6 @@ private CodeInfo() _analyzer = null; } - protected CodeInfo(IPrecompile precompile, int version, ReadOnlyMemory code) - { - Precompile = precompile; - Version = version; - Code = code; - _analyzer = null; - } - - // Eof - protected CodeInfo(int version, ReadOnlyMemory code) - { - Version = version; - Code = code; - _analyzer = null; - } - // Regular contract public CodeInfo(ReadOnlyMemory code) { @@ -63,12 +47,6 @@ public CodeInfo(IPrecompile? precompile) public bool ValidateJump(int destination) => _analyzer?.ValidateJump(destination) ?? false; - /// - /// Gets the version of the code format. - /// The default implementation returns 0, representing a legacy code format or non-EOF code. - /// - public int Version { get; } = 0; - void IThreadPoolWorkItem.Execute() => _analyzer?.Execute(); diff --git a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs index ea44bf69e967..6bcb05e5a198 100644 --- a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs @@ -4,7 +4,6 @@ using System; using Nethermind.Core; using Nethermind.Core.Specs; -using Nethermind.Evm.EvmObjectFormat; namespace Nethermind.Evm { @@ -46,24 +45,10 @@ public static bool CalculateCost(IReleaseSpec spec, int byteCodeLength, out long return true; } - public static bool CodeIsInvalid(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion) - => !CodeIsValid(spec, code, fromVersion); + public static bool CodeIsInvalid(IReleaseSpec spec, ReadOnlyMemory code) + => !CodeIsValid(spec, code); - public static bool CodeIsValid(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion) - => spec.IsEofEnabled ? IsValidWithEofRules(spec, code, fromVersion) : IsValidWithLegacyRules(spec, code); - - public static bool IsValidWithLegacyRules(IReleaseSpec spec, ReadOnlyMemory code) + public static bool CodeIsValid(IReleaseSpec spec, ReadOnlyMemory code) => !spec.IsEip3541Enabled || !code.StartsWith(InvalidStartingCodeByte); - - public static bool IsValidWithEofRules(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion, ValidationStrategy strategy = ValidationStrategy.Validate) - { - byte codeVersion = 0; - bool isCodeEof = code.Length >= EofValidator.MAGIC.Length && EofValidator.IsEof(code, out codeVersion); - bool valid = codeVersion >= fromVersion - && (isCodeEof - ? (fromVersion > 0 && EofValidator.IsValidEof(code, strategy, out _)) - : (fromVersion == 0 && IsValidWithLegacyRules(spec, code))); - return valid; - } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index 91ff0753e0e1..731d84866887 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -2,46 +2,15 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Diagnostics.CodeAnalysis; -using Nethermind.Core.Specs; -using Nethermind.Evm.EvmObjectFormat; namespace Nethermind.Evm.CodeAnalysis; public static class CodeInfoFactory { - public static CodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, ValidationStrategy validationRules = ValidationStrategy.ExtractHeader) + public static CodeInfo CreateCodeInfo(ReadOnlyMemory code) { - if (spec.IsEofEnabled - && code.Span.StartsWith(EofValidator.MAGIC) - && EofValidator.IsValidEof(code, validationRules, out EofContainer? container)) - { - return new EofCodeInfo(container.Value); - } CodeInfo codeInfo = new(code); codeInfo.AnalyzeInBackgroundIfRequired(); return codeInfo; } - - public static bool CreateInitCodeInfo(ReadOnlyMemory data, IReleaseSpec spec, [NotNullWhen(true)] out CodeInfo? codeInfo, out ReadOnlyMemory extraCallData) - { - extraCallData = default; - if (spec.IsEofEnabled && data.Span.StartsWith(EofValidator.MAGIC)) - { - if (EofValidator.IsValidEof(data, ValidationStrategy.ValidateInitCodeMode | ValidationStrategy.ValidateFullBody | ValidationStrategy.AllowTrailingBytes, out EofContainer? eofContainer)) - { - int containerSize = eofContainer.Value.Header.DataSection.EndOffset; - extraCallData = data[containerSize..]; - codeInfo = new EofCodeInfo(eofContainer.Value); - return true; - } - codeInfo = null; - return false; - } - - CodeInfo legacyCodeInfo = new(data); - legacyCodeInfo.AnalyzeInBackgroundIfRequired(); - codeInfo = legacyCodeInfo; - return true; - } } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index b086c06949bf..72bae0df484f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -23,7 +23,7 @@ public class CodeInfoRepository : ICodeInfoRepository { private readonly FrozenDictionary _localPrecompiles; private readonly IWorldState _worldState; - private readonly Func _codeInfoLoader; + private readonly Func _codeInfoLoader; private readonly IBlockAccessListBuilder? _balBuilder; public CodeInfoRepository(IWorldState worldState, IPrecompileProvider precompileProvider) @@ -31,15 +31,15 @@ public CodeInfoRepository(IWorldState worldState, IPrecompileProvider precompile { } - internal CodeInfoRepository(IWorldState worldState, IPrecompileProvider precompileProvider, Func? codeInfoLoader) + internal CodeInfoRepository(IWorldState worldState, IPrecompileProvider precompileProvider, Func? codeInfoLoader) { _localPrecompiles = precompileProvider.GetPrecompiles(); _worldState = worldState; _balBuilder = _worldState as IBlockAccessListBuilder; _codeInfoLoader = codeInfoLoader ?? DefaultLoad; - CodeInfo DefaultLoad(ValueHash256 codeHash, IReleaseSpec spec) => - codeHash == ValueKeccak.OfAnEmptyString ? CodeInfo.Empty : GetCodeInfo(worldState, in codeHash, spec); + CodeInfo DefaultLoad(ValueHash256 codeHash) => + codeHash == ValueKeccak.OfAnEmptyString ? CodeInfo.Empty : GetCodeInfo(worldState, in codeHash); } public CodeInfo GetCachedCodeInfo(Address codeSource, bool followDelegation, IReleaseSpec vmSpec, out Address? delegationAddress) @@ -54,26 +54,26 @@ public CodeInfo GetCachedCodeInfo(Address codeSource, bool followDelegation, IRe return _localPrecompiles[codeSource]; } - CodeInfo codeInfo = InternalGetCodeInfo(codeSource, vmSpec); + CodeInfo codeInfo = InternalGetCodeInfo(codeSource); if (!codeInfo.IsEmpty && ICodeInfoRepository.TryGetDelegatedAddress(codeInfo.CodeSpan, out delegationAddress)) { if (followDelegation) { - codeInfo = InternalGetCodeInfo(delegationAddress, vmSpec); + codeInfo = InternalGetCodeInfo(delegationAddress); } } return codeInfo; } - private CodeInfo InternalGetCodeInfo(Address codeSource, IReleaseSpec vmSpec) + private CodeInfo InternalGetCodeInfo(Address codeSource) { ref readonly ValueHash256 codeHash = ref _worldState.GetCodeHash(codeSource); - return _codeInfoLoader(codeHash, vmSpec); + return _codeInfoLoader(codeHash); } - internal static CodeInfo GetCodeInfo(IWorldState worldState, in ValueHash256 codeHash, IReleaseSpec vmSpec) + internal static CodeInfo GetCodeInfo(IWorldState worldState, in ValueHash256 codeHash) { byte[]? code = worldState.GetCode(in codeHash); if (code is null) @@ -81,7 +81,7 @@ internal static CodeInfo GetCodeInfo(IWorldState worldState, in ValueHash256 cod MissingCode(in codeHash); } - return CodeInfoFactory.CreateCodeInfo(code, vmSpec); + return CodeInfoFactory.CreateCodeInfo(code); [DoesNotReturn, StackTraceHidden] static void MissingCode(in ValueHash256 codeHash) => throw new DataException($"Code {codeHash} missing in the state"); @@ -137,12 +137,12 @@ public ValueHash256 GetExecutableCodeHash(Address address, IReleaseSpec spec) return Keccak.OfAnEmptyString.ValueHash256; } - CodeInfo codeInfo = _codeInfoLoader(codeHash, spec); + CodeInfo codeInfo = _codeInfoLoader(codeHash); return codeInfo.IsEmpty ? Keccak.OfAnEmptyString.ValueHash256 : codeHash; } public bool TryGetDelegation(Address address, IReleaseSpec spec, [NotNullWhen(true)] out Address? delegatedAddress) => - ICodeInfoRepository.TryGetDelegatedAddress(InternalGetCodeInfo(address, spec).CodeSpan, out delegatedAddress); + ICodeInfoRepository.TryGetDelegatedAddress(InternalGetCodeInfo(address).CodeSpan, out delegatedAddress); } diff --git a/src/Nethermind/Nethermind.Evm/EvmException.cs b/src/Nethermind/Nethermind.Evm/EvmException.cs index 7a32f3f07340..4b0b25eb4ac5 100644 --- a/src/Nethermind/Nethermind.Evm/EvmException.cs +++ b/src/Nethermind/Nethermind.Evm/EvmException.cs @@ -18,12 +18,8 @@ public enum EvmExceptionType StackOverflow, StackUnderflow, OutOfGas, - GasUInt64Overflow, - InvalidSubroutineEntry, - InvalidSubroutineReturn, InvalidJumpDestination, AccessViolation, - AddressOutOfRange, StaticCallViolation, PrecompileFailure, TransactionCollision, diff --git a/src/Nethermind/Nethermind.Evm/EvmExceptionExtensions.cs b/src/Nethermind/Nethermind.Evm/EvmExceptionExtensions.cs index 74aa1b7e2330..bd77cd53d161 100644 --- a/src/Nethermind/Nethermind.Evm/EvmExceptionExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/EvmExceptionExtensions.cs @@ -13,9 +13,6 @@ public static class EvmExceptionExtensions EvmExceptionType.StackOverflow => "max call depth exceeded", EvmExceptionType.StackUnderflow => "stack underflow", EvmExceptionType.OutOfGas => "out of gas", - EvmExceptionType.GasUInt64Overflow => "gas uint64 overflow", - EvmExceptionType.InvalidSubroutineEntry => "invalid jump destination", - EvmExceptionType.InvalidSubroutineReturn => "invalid jump destination", EvmExceptionType.InvalidJumpDestination => "invalid jump destination", EvmExceptionType.AccessViolation => "return data out of bounds", EvmExceptionType.StaticCallViolation => "write protection", diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs deleted file mode 100644 index 82e5a8ee5b95..000000000000 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using Nethermind.Evm.EvmObjectFormat; - -namespace Nethermind.Evm.CodeAnalysis; - -public sealed class EofCodeInfo : CodeInfo -{ - public EofCodeInfo(in EofContainer container) : base(container.Header.Version, container.Container) - { - EofContainer = container; - } - - public EofContainer EofContainer { get; private set; } - public ReadOnlyMemory TypeSection => EofContainer.TypeSection; - public ReadOnlyMemory CodeSection => EofContainer.CodeSection; - public ReadOnlyMemory DataSection => EofContainer.DataSection; - public ReadOnlyMemory ContainerSection => EofContainer.ContainerSection; - - public SectionHeader CodeSectionOffset(int sectionId) => EofContainer.Header.CodeSections[sectionId]; - public SectionHeader? ContainerSectionOffset(int sectionId) => EofContainer.Header.ContainerSections.Value[sectionId]; - public int PcOffset() => EofContainer.Header.CodeSections.Start; - - public (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) - => EofContainer.GetSectionMetadata(index); -} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs deleted file mode 100644 index 60a355a85ced..000000000000 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using Nethermind.Evm.EvmObjectFormat.Handlers; -using Nethermind.Logging; - -[assembly: InternalsVisibleTo("Nethermind.EofParser")] -[assembly: InternalsVisibleTo("Ethereum.Test.Base")] - -namespace Nethermind.Evm.EvmObjectFormat; - -public static class EofValidator -{ - // magic prefix : EofFormatByte is the first byte, EofFormatDiff is chosen to diff from previously rejected contract according to EIP3541 - public static ReadOnlySpan MAGIC => [0xEF, 0x00]; - public const byte ONE_BYTE_LENGTH = 1; - public const byte TWO_BYTE_LENGTH = 2; - public const byte VERSION_OFFSET = TWO_BYTE_LENGTH; // magic length - - private static readonly Dictionary _eofVersionHandlers = []; - internal static ILogger Logger { get; set; } = NullLogger.Instance; - - static EofValidator() - { - _eofVersionHandlers.Add(Eof1.VERSION, new Eof1()); - } - - /// - /// returns whether the code passed is supposed to be treated as Eof regardless of its validity. - /// - /// Machine code to be checked - /// - public static bool IsEof(ReadOnlyMemory container, [NotNullWhen(true)] out byte version) - => IsEof(container.Span, out version); - - /// - /// returns whether the code passed is supposed to be treated as Eof regardless of its validity. - /// - /// Machine code to be checked - /// - public static bool IsEof(ReadOnlySpan container, [NotNullWhen(true)] out byte version) - { - if (container.Length > MAGIC.Length) - { - version = container[MAGIC.Length]; - return container.StartsWith(MAGIC); - } - else - { - version = 0; - return false; - } - } - - public static bool IsValidEofHeader(ReadOnlyMemory code, [NotNullWhen(true)] out EofHeader? header) - { - if (IsEof(code, out byte version) && _eofVersionHandlers.TryGetValue(version, out IEofVersionHandler handler)) - { - return handler.TryParseEofHeader(code, ValidationStrategy.Validate, out header); - } - - if (Logger.IsTrace) Logger.Trace($"EOF: Eof not recognized"); - header = null; - return false; - } - - public static bool IsValidEof(ReadOnlyMemory code, ValidationStrategy strategy, [NotNullWhen(true)] out EofContainer? eofContainer) - { - if (strategy == ValidationStrategy.None) - { - if (Logger.IsTrace) Logger.Trace($"EOF: No validation"); - eofContainer = null; - return true; - } - - if (strategy.HasFlag(ValidationStrategy.HasEofMagic) && !code.Span.StartsWith(MAGIC)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: No MAGIC as start of code"); - eofContainer = null; - return false; - } - - if (IsEof(code, out byte version) && _eofVersionHandlers.TryGetValue(version, out IEofVersionHandler handler)) - { - return handler.TryGetEofContainer(strategy, out eofContainer, code); - } - - if (Logger.IsTrace) Logger.Trace($"EOF: Not EOF"); - eofContainer = null; - return false; - } -} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs deleted file mode 100644 index 68c0819278a5..000000000000 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Linq; -using Nethermind.Core.Extensions; -using Nethermind.Evm.EvmObjectFormat.Handlers; - -namespace Nethermind.Evm.EvmObjectFormat; - -public readonly struct EofContainer -{ - public readonly ReadOnlyMemory Container; - public bool IsEmpty => Container.IsEmpty; - - // https://eips.ethereum.org/EIPS/eip-3540#section-structure - public EofContainer(ReadOnlyMemory container, EofHeader eofHeader) - { - Container = container; - Header = eofHeader; - Prefix = container[..eofHeader.PrefixSize]; - TypeSection = container[(Range)eofHeader.TypeSection]; - CodeSection = container[(Range)eofHeader.CodeSections]; - ContainerSection = eofHeader.ContainerSections.HasValue ? container[(Range)eofHeader.ContainerSections.Value] : ReadOnlyMemory.Empty; - - TypeSections = new ReadOnlyMemory[eofHeader.CodeSections.Count]; - for (var i = 0; i < eofHeader.CodeSections.Count; i++) - { - TypeSections[i] = TypeSection.Slice(i * Eof1.MINIMUM_TYPESECTION_SIZE, Eof1.MINIMUM_TYPESECTION_SIZE); - } - - CodeSections = new ReadOnlyMemory[eofHeader.CodeSections.Count]; - for (var i = 0; i < eofHeader.CodeSections.Count; i++) - { - CodeSections[i] = CodeSection[(Range)Header.CodeSections[i]]; - } - - if (eofHeader.ContainerSections.HasValue) - { - ContainerSections = new ReadOnlyMemory[eofHeader.ContainerSections.Value.Count]; - for (var i = 0; i < eofHeader.ContainerSections.Value.Count; i++) - { - ContainerSections[i] = ContainerSection[(Range)Header.ContainerSections.Value[i]]; - } - } - else - { - ContainerSections = Array.Empty>(); - } - - DataSection = container[eofHeader.DataSection.Start..]; - } - - public readonly EofHeader Header; - public readonly ReadOnlyMemory Prefix; - - public readonly ReadOnlyMemory TypeSection; - public readonly ReadOnlyMemory[] TypeSections; - - public readonly ReadOnlyMemory CodeSection; - public readonly ReadOnlyMemory[] CodeSections; - - public readonly ReadOnlyMemory ContainerSection; - public readonly ReadOnlyMemory[] ContainerSections; - public readonly ReadOnlyMemory DataSection; - - public (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) - { - ReadOnlySpan typeSection = TypeSections[index].Span; - return - ( - typeSection[Eof1.INPUTS_OFFSET], - typeSection[Eof1.OUTPUTS_OFFSET], - typeSection.Slice(Eof1.MAX_STACK_HEIGHT_OFFSET, Eof1.MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16() - ); - } -} - -public struct EofHeader() -{ - public required byte Version; - public required int PrefixSize; - public required SectionHeader TypeSection; - public required CompoundSectionHeader CodeSections; - public required CompoundSectionHeader? ContainerSections; - public required SectionHeader DataSection; -} - -public readonly struct SectionHeader(int start, ushort size) -{ - public readonly int Start => start; - public readonly int Size => size; - public readonly int EndOffset => Start + Size; - - public static implicit operator Range(SectionHeader section) => new(section.Start, section.EndOffset); -} - -public readonly struct CompoundSectionHeader(int start, int[] subSectionsSizes) -{ - public readonly int Start => start; - - public readonly int[] SubSectionsSizes = subSectionsSizes; - - public readonly int EndOffset => Start + SubSectionsSizes.Sum(); - public readonly int Size => EndOffset - Start; - public readonly int Count => SubSectionsSizes.Length; - - private static int[] CreateSubSectionsSizes(int[] subSectionsSizes) - { - var subSectionsSizesAcc = new int[subSectionsSizes.Length]; - subSectionsSizesAcc[0] = 0; - for (var i = 1; i < subSectionsSizes.Length; i++) - { - subSectionsSizesAcc[i] = subSectionsSizesAcc[i - 1] + subSectionsSizes[i - 1]; - } - - return subSectionsSizesAcc; - } - - private int[] SubSectionsSizesAcc { get; } = CreateSubSectionsSizes(subSectionsSizes); - - public SectionHeader this[int i] => new(SubSectionsSizesAcc[i], (ushort)SubSectionsSizes[i]); - - public static implicit operator Range(CompoundSectionHeader section) => new(section.Start, section.EndOffset); -} - diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs deleted file mode 100644 index 96d2aa034887..000000000000 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; - -namespace Nethermind.Evm.EvmObjectFormat; - -[Flags] -public enum ValidationStrategy : byte -{ - None = 0, - Validate = 1, - FullBody = 2, - InitCodeMode = 4, - RuntimeMode = 8, - AllowTrailingBytes = 16, - ValidateFullBody = Validate | FullBody, - ValidateInitCodeMode = Validate | InitCodeMode, - ValidateRuntimeMode = Validate | RuntimeMode, - ExtractHeader = 32, - HasEofMagic = 64, -} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs deleted file mode 100644 index b363652df5d4..000000000000 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ /dev/null @@ -1,1941 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using FastEnumUtility; -using Nethermind.Core.Extensions; -using static Nethermind.Evm.EvmObjectFormat.EofValidator; - -namespace Nethermind.Evm.EvmObjectFormat.Handlers; - -// https://github.com/ipsilon/eof/blob/main/spec/eof.md -internal class Eof1 : IEofVersionHandler -{ - public const byte VERSION = 0x01; - - internal const byte MINIMUM_HEADER_SECTION_SIZE = 3; - internal const byte MINIMUM_TYPESECTION_SIZE = 4; - internal const byte MINIMUM_CODESECTION_SIZE = 1; - internal const byte MINIMUM_DATASECTION_SIZE = 0; - internal const byte MINIMUM_CONTAINERSECTION_SIZE = 0; - internal const byte MINIMUM_HEADER_SIZE = VERSION_OFFSET - + MINIMUM_HEADER_SECTION_SIZE - + MINIMUM_HEADER_SECTION_SIZE + TWO_BYTE_LENGTH - + MINIMUM_HEADER_SECTION_SIZE - + ONE_BYTE_LENGTH; - - internal const byte BYTE_BIT_COUNT = 8; // indicates the length of the count immediate of JumpV - internal const byte MINIMUMS_ACCEPTABLE_JUMPV_JUMPTABLE_LENGTH = 1; // indicates the length of the count immediate of JumpV - - internal const byte INPUTS_OFFSET = 0; - internal const byte INPUTS_MAX = 0x7F; - - internal const byte OUTPUTS_OFFSET = INPUTS_OFFSET + 1; - internal const byte OUTPUTS_MAX = 0x7F; - internal const byte NON_RETURNING = 0x80; - - internal const byte MAX_STACK_HEIGHT_OFFSET = OUTPUTS_OFFSET + 1; - internal const int MAX_STACK_HEIGHT_LENGTH = 2; - internal const ushort MAX_STACK_HEIGHT = 0x400; - - internal const ushort MINIMUM_NUM_CODE_SECTIONS = 1; - internal const ushort MAXIMUM_NUM_CODE_SECTIONS = 1024; - internal const int MAXIMUM_NUM_CONTAINER_SECTIONS = 0x00FF; - internal const ushort RETURN_STACK_MAX_HEIGHT = MAXIMUM_NUM_CODE_SECTIONS; // the size in the type section allocated to each function section - - internal const ushort MINIMUM_SIZE = MINIMUM_HEADER_SIZE - + MINIMUM_TYPESECTION_SIZE // minimum type section body size - + MINIMUM_CODESECTION_SIZE // minimum code section body size - + MINIMUM_DATASECTION_SIZE; // minimum data section body size - - // EIP-3540 ties this to MAX_INIT_CODE_SIZE from EIP-3860, but we need a constant here - internal const ushort MAXIMUM_SIZE = 0xc000; - - /// - /// Attempts to parse the EOF header from the provided container memory. - /// - /// - /// The memory containing the raw EOF data to parse. - /// - /// - /// Flags that control additional validation (for example, whether to allow trailing bytes). - /// - /// - /// When this method returns, contains the parsed header if successful; otherwise, null. - /// - /// - /// true if the header was successfully parsed; otherwise, false. - /// - public bool TryParseEofHeader(ReadOnlyMemory containerMemory, ValidationStrategy validationStrategy, out EofHeader? header) - { - header = null; - ReadOnlySpan container = containerMemory.Span; - - // Validate overall container size, magic value, and version. - if (!ValidateBasicConstraints(container)) - { - return false; - } - - // The current read position; after the version byte. - int pos = VERSION_OFFSET + 1; - - // Holds header size information for each section. - Sizes sectionSizes = new(); - - // These arrays hold the sizes for compound sections. - int[]? codeSections = null; - int[]? containerSections = null; - - bool continueParsing = true; - while (continueParsing && pos < container.Length) - { - // Read the next separator that indicates which section comes next. - Separator separator = (Separator)container[pos++]; - switch (separator) - { - case Separator.KIND_TYPE: - if (sectionSizes.TypeSectionSize != null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Multiple type sections"); - return false; - } - if (!TryParseTypeSection(ref pos, out ushort typeSectionSize, container)) - { - return false; - } - sectionSizes.TypeSectionSize = typeSectionSize; - break; - - case Separator.KIND_CODE: - if (sectionSizes.CodeSectionSize != null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Multiple code sections"); - return false; - } - if (sectionSizes.TypeSectionSize is null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); - return false; - } - if (!TryParseCodeSection(ref pos, out codeSections, out ushort codeHeaderSize, container)) - { - return false; - } - sectionSizes.CodeSectionSize = codeHeaderSize; - break; - - case Separator.KIND_CONTAINER: - if (sectionSizes.ContainerSectionSize != null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Multiple container sections"); - return false; - } - if (sectionSizes.CodeSectionSize is null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); - return false; - } - if (sectionSizes.DataSectionSize != null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Container section is out of order"); - return false; - } - if (!TryParseContainerSection(ref pos, out containerSections, out ushort containerHeaderSize, container)) - { - return false; - } - sectionSizes.ContainerSectionSize = containerHeaderSize; - break; - - case Separator.KIND_DATA: - if (sectionSizes.DataSectionSize != null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Multiple data sections"); - return false; - } - if (sectionSizes.CodeSectionSize is null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); - return false; - } - if (!TryParseDataSection(ref pos, out ushort dataSectionSize, container)) - { - return false; - } - sectionSizes.DataSectionSize = dataSectionSize; - break; - - case Separator.TERMINATOR: - // The terminator must be followed by at least one byte. - if (container.Length < pos + ONE_BYTE_LENGTH) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - continueParsing = false; - break; - - default: - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code header is not well formatted"); - return false; - } - } - - // Make sure mandatory sections (type, code, and data) were found. - if (sectionSizes.TypeSectionSize is null || sectionSizes.CodeSectionSize is null || sectionSizes.DataSectionSize is null) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); - return false; - } - - // Build the sub-headers for the various sections. - var typeSectionHeader = new SectionHeader(pos, sectionSizes.TypeSectionSize.Value); - var codeSectionHeader = new CompoundSectionHeader(typeSectionHeader.EndOffset, codeSections); - CompoundSectionHeader? containerSectionHeader = containerSections is null ? null : - new CompoundSectionHeader(codeSectionHeader.EndOffset, containerSections); - var dataSectionHeader = new SectionHeader(containerSectionHeader?.EndOffset ?? codeSectionHeader.EndOffset, - sectionSizes.DataSectionSize.Value); - - // Validate that the container does not have extra trailing bytes (unless allowed) and that - // the data section is fully contained. - if (!validationStrategy.HasFlag(ValidationStrategy.AllowTrailingBytes) && - dataSectionHeader.EndOffset < containerMemory.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Extra data after end of container, starting at {dataSectionHeader.EndOffset}"); - return false; - } - if (validationStrategy.HasFlag(ValidationStrategy.Validate) && - !validationStrategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) && - dataSectionHeader.EndOffset > container.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Container has truncated data where full data is required"); - return false; - } - - header = new EofHeader - { - Version = VERSION, - PrefixSize = pos, - TypeSection = typeSectionHeader, - CodeSections = codeSectionHeader, - ContainerSections = containerSectionHeader, - DataSection = dataSectionHeader, - }; - - return true; - } - - /// - /// Validates overall constraints for the container (size, magic header, and version). - /// - /// The container data as a span. - /// true if the basic constraints pass; otherwise, false. - private static bool ValidateBasicConstraints(ReadOnlySpan container) - { - if (container.Length < MINIMUM_SIZE) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - if (container.Length > MAXIMUM_SIZE) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is larger than allowed maximum size of {MAXIMUM_SIZE}"); - return false; - } - if (!container.StartsWith(MAGIC)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code doesn't start with magic byte sequence expected {MAGIC.ToHexString(true)} "); - return false; - } - if (container[VERSION_OFFSET] != VERSION) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is not Eof version {VERSION}"); - return false; - } - return true; - } - - /// - /// Parses the type section header. - /// - /// - /// The current read position. On success, this is advanced past the type section header. - /// - /// The size of the type section as read from the header. - /// The container span. - /// true if the type section header was parsed successfully; otherwise, false. - private static bool TryParseTypeSection(ref int pos, out ushort typeSectionSize, ReadOnlySpan container) - { - typeSectionSize = 0; - // Ensure enough bytes are available to read the type section size. - if (container.Length < pos + TWO_BYTE_LENGTH) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - typeSectionSize = GetUInt16(pos, container); - if (typeSectionSize < MINIMUM_TYPESECTION_SIZE) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, TypeSection Size must be at least {MINIMUM_TYPESECTION_SIZE}, but found {typeSectionSize}"); - return false; - } - pos += TWO_BYTE_LENGTH; - return true; - } - - /// - /// Parses the code section header and its list of section sizes. - /// - /// - /// The current read position. On success, this is advanced past the code section header. - /// - /// - /// On success, the array of individual code section sizes. - /// - /// - /// On success, the total header size (in bytes) for the code section. - /// - /// The container span. - /// true if the code section header was parsed successfully; otherwise, false. - private static bool TryParseCodeSection(ref int pos, out int[]? codeSections, out ushort headerSize, ReadOnlySpan container) - { - codeSections = null; - headerSize = 0; - // Must have enough bytes to read the count of code sections. - if (container.Length < pos + TWO_BYTE_LENGTH) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - - ushort numberOfCodeSections = GetUInt16(pos, container); - headerSize = (ushort)(numberOfCodeSections * TWO_BYTE_LENGTH); - - if (numberOfCodeSections > MAXIMUM_NUM_CODE_SECTIONS) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, code sections count must not exceed {MAXIMUM_NUM_CODE_SECTIONS}"); - return false; - } - - int requiredLength = pos + TWO_BYTE_LENGTH + (numberOfCodeSections * TWO_BYTE_LENGTH); - if (container.Length < requiredLength) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - - codeSections = new int[numberOfCodeSections]; - int headerStart = pos + TWO_BYTE_LENGTH; - for (int i = 0; i < codeSections.Length; i++) - { - int currentOffset = headerStart + (i * TWO_BYTE_LENGTH); - int codeSectionSize = GetUInt16(currentOffset, container); - if (codeSectionSize == 0) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Empty Code Section are not allowed, CodeSectionSize must be > 0 but found {codeSectionSize}"); - return false; - } - codeSections[i] = codeSectionSize; - } - pos += TWO_BYTE_LENGTH + (numberOfCodeSections * TWO_BYTE_LENGTH); - return true; - } - - /// - /// Parses the container section header and its list of section sizes. - /// - /// - /// The current read position. On success, this is advanced past the container section header. - /// - /// - /// On success, the array of individual container section sizes. - /// - /// - /// On success, the total header size (in bytes) for the container section. - /// - /// The container span. - /// true if the container section header was parsed successfully; otherwise, false. - private static bool TryParseContainerSection(ref int pos, out int[]? containerSections, out ushort headerSize, ReadOnlySpan container) - { - containerSections = null; - headerSize = 0; - if (container.Length < pos + TWO_BYTE_LENGTH) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - - ushort numberOfContainerSections = GetUInt16(pos, container); - headerSize = (ushort)(numberOfContainerSections * TWO_BYTE_LENGTH); - - // Enforce that the count is not zero and does not exceed the maximum allowed. - if (numberOfContainerSections == 0 || numberOfContainerSections > (MAXIMUM_NUM_CONTAINER_SECTIONS + 1)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, container sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); - return false; - } - - int requiredLength = pos + TWO_BYTE_LENGTH + (numberOfContainerSections * TWO_BYTE_LENGTH); - if (container.Length < requiredLength) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - - containerSections = new int[numberOfContainerSections]; - int headerStart = pos + TWO_BYTE_LENGTH; - for (int i = 0; i < containerSections.Length; i++) - { - int currentOffset = headerStart + (i * TWO_BYTE_LENGTH); - int containerSectionSize = GetUInt16(currentOffset, container); - if (containerSectionSize == 0) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Empty Container Section are not allowed, containerSectionSize must be > 0 but found {containerSectionSize}"); - return false; - } - containerSections[i] = containerSectionSize; - } - pos += TWO_BYTE_LENGTH + (numberOfContainerSections * TWO_BYTE_LENGTH); - return true; - } - - /// - /// Parses the data section header. - /// - /// - /// The current read position. On success, this is advanced past the data section header. - /// - /// - /// On success, the size of the data section as specified in the header. - /// - /// The container span. - /// true if the data section header was parsed successfully; otherwise, false. - private static bool TryParseDataSection(ref int pos, out ushort dataSectionSize, ReadOnlySpan container) - { - dataSectionSize = 0; - if (container.Length < pos + TWO_BYTE_LENGTH) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - dataSectionSize = GetUInt16(pos, container); - pos += TWO_BYTE_LENGTH; - return true; - } - - /// - /// Reads an unsigned 16-bit integer from the specified container at the given offset. - /// - /// The offset within the span to read from. - /// The byte span containing the data. - /// The 16‐bit unsigned integer value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort GetUInt16(int offset, ReadOnlySpan container) => - container.Slice(offset, TWO_BYTE_LENGTH).ReadEthUInt16(); - - /// - /// Attempts to create an from the provided raw code data. - /// - /// - /// The flags indicating which validation steps to perform (e.g. whether full container validation is required). - /// - /// - /// When this method returns, contains the parsed if successful; otherwise, null. - /// - /// - /// The raw code data as a . - /// - /// - /// true if a valid EOF container was created from the code; otherwise, false. - /// - public bool TryGetEofContainer(ValidationStrategy validationStrategy, [NotNullWhen(true)] out EofContainer? eofContainer, ReadOnlyMemory code) - { - eofContainer = null; - - // Step 1: Attempt to parse the header from the code. - if (!TryParseEofHeader(code, validationStrategy, out EofHeader? header)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Header not parsed"); - return false; - } - - // Step 2: Validate the body using the parsed header and the full code span. - if (!ValidateBody(header.Value, validationStrategy, code.Span)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Body not valid"); - return false; - } - - // Step 3: Construct the container using the raw code and the parsed header. - eofContainer = new EofContainer(code, header.Value); - - // Step 4: If full validation is requested, verify the container's integrity. - if (validationStrategy.HasFlag(ValidationStrategy.Validate)) - { - if (!ValidateContainer(eofContainer.Value, validationStrategy)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Container not valid"); - return false; - } - } - - return true; - } - - /// - /// Validates the integrity of the given EOF container, including its nested container sections, - /// according to the provided . - /// - /// The EOF container to validate. - /// The strategy flags controlling the level of validation. - /// true if the container and all its nested sections are valid; otherwise, false. - private bool ValidateContainer(in EofContainer eofContainer, ValidationStrategy validationStrategy) - { - // Use a FIFO queue to process nested containers. - // Each entry pairs a container with its associated validation strategy. - Queue<(EofContainer container, ValidationStrategy strategy)> containers = new(); - containers.Enqueue((eofContainer, validationStrategy)); - - // Process each container (including nested ones) until none remain. - while (containers.TryDequeue(out (EofContainer container, ValidationStrategy strategy) target)) - { - // Process the current container. If validation fails at any level, return false. - if (!ProcessContainer(target.container, target.strategy, containers)) - { - return false; - } - } - - return true; - } - - /// - /// Processes a single container by validating its code sections and any nested container sections. - /// - /// The container to process. - /// The validation strategy to apply. - /// - /// A queue into which any nested container (subsection) that requires further validation will be enqueued. - /// - /// true if all sections in the container are valid; otherwise, false. - private bool ProcessContainer(in EofContainer targetContainer, ValidationStrategy strategy, - Queue<(EofContainer container, ValidationStrategy strategy)> containers) - { - // Create a work queue to traverse the container’s sections. - // The capacity is 1 (for the main container’s code sections) plus any nested container sections. - QueueManager containerQueue = new(1 + (targetContainer.Header.ContainerSections?.Count ?? 0)); - - // Enqueue index 0 to represent the main container's code section. - containerQueue.Enqueue(0, strategy); - containerQueue.VisitedContainers[0] = GetValidation(strategy); - - // Process each work item in the container queue. - while (containerQueue.TryDequeue(out (int Index, ValidationStrategy Strategy) worklet)) - { - // Worklet index 0 represents the primary container's code sections. - if (worklet.Index == 0) - { - if (!ValidateCodeSections(targetContainer, worklet.Strategy, in containerQueue)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code sections invalid"); - return false; - } - } - else - { - // Process a nested container section. - if (!ProcessContainerSection(targetContainer, worklet.Index, worklet.Strategy, containers)) - { - return false; - } - } - - // Mark the current worklet as visited using a helper value derived from the strategy. - containerQueue.MarkVisited(worklet.Index, GetVisited(worklet.Strategy)); - } - - // After processing, all expected work items must have been visited. - if (!containerQueue.IsAllVisitedAndNotAmbiguous()) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Not all containers visited"); - return false; - } - - return true; - } - - /// - /// Processes a container section (i.e. a nested container) by parsing its header and validating its body. - /// If the current strategy indicates full validation, the new container is enqueued for further processing. - /// - /// The parent container that holds the container sections. - /// - /// The worklet index representing the container section. (A value of 1 corresponds to the first container section.) - /// - /// The validation strategy to apply. - /// - /// The queue to which a newly constructed nested container will be enqueued if further validation is required. - /// - /// true if the container section is valid or skipped; otherwise, false. - private bool ProcessContainerSection(in EofContainer targetContainer, int workletIndex, - ValidationStrategy strategy, - Queue<(EofContainer container, ValidationStrategy strategy)> containers) - { - // If the worklet index exceeds the number of available container sections, skip processing. - if (targetContainer.ContainerSections.Length < workletIndex) - return true; - - // Adjust the section index (worklet indices start at 1 for container sections). - int section = workletIndex - 1; - ReadOnlyMemory subsection = targetContainer.ContainerSections[section]; - - // Parse the header for the nested container section. - if (!TryParseEofHeader(subsection, strategy, out EofHeader? header)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Header invalid: section {section}"); - return false; - } - - // Validate the body of the nested container section. - if (!ValidateBody(header.Value, strategy, subsection.Span)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Body invalid: section {section}"); - return false; - } - - // If full validation is required, enqueue the new nested container for further processing. - if (strategy.HasFlag(ValidationStrategy.Validate)) - { - containers.Enqueue((new EofContainer(subsection, header.Value), strategy)); - } - - return true; - } - - private static ValidationStrategy GetVisited(ValidationStrategy validationStrategy) => validationStrategy.HasFlag(ValidationStrategy.ValidateInitCodeMode) - ? ValidationStrategy.ValidateInitCodeMode - : ValidationStrategy.ValidateRuntimeMode; - - private static ValidationStrategy GetValidation(ValidationStrategy validationStrategy) => validationStrategy.HasFlag(ValidationStrategy.ValidateInitCodeMode) - ? ValidationStrategy.ValidateInitCodeMode - : validationStrategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) - ? ValidationStrategy.ValidateRuntimeMode - : ValidationStrategy.None; - - /// - /// Validates the body of the EOF container, ensuring that the various sections (code, data, and type) - /// are consistent with the metadata specified in the header. - /// - /// - /// The parsed EOF header containing metadata about section boundaries and sizes. - /// - /// - /// The flags controlling which validation rules are applied. - /// - /// - /// The complete container data as a read-only span of bytes. - /// - /// - /// true if the body is valid according to the header and strategy; otherwise, false. - /// - private static bool ValidateBody(in EofHeader header, ValidationStrategy strategy, ReadOnlySpan container) - { - // 1. Validate overall offsets and the contract (code) body length. - int startOffset = header.TypeSection.Start; - int endOffset = header.DataSection.Start; - - // Ensure the DataSection starts within the container. - if (endOffset > container.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, DataSectionStart ({endOffset}) exceeds container length ({container.Length})."); - return false; - } - - // Calculate the expected length of the "contract body" (combined code sections) - int calculatedCodeLength = header.TypeSection.Size - + header.CodeSections.Size - + (header.ContainerSections?.Size ?? 0); - - // Extract the contract body and the data body segments. - ReadOnlySpan contractBody = container[startOffset..endOffset]; - ReadOnlySpan dataBody = container[endOffset..]; - - // The contract body length must exactly match the sum of the sizes indicated in the header. - if (contractBody.Length != calculatedCodeLength) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Contract body length ({contractBody.Length}) does not match calculated code length ({calculatedCodeLength})."); - return false; - } - - // 2. Validate the container sections count. - // Is one extra from the initial so one extra to max count - if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS + 1) - { - // NOTE: This check could be moved to the header parsing phase. - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Container sections count ({header.ContainerSections?.Count}) exceeds allowed maximum ({MAXIMUM_NUM_CONTAINER_SECTIONS})."); - return false; - } - - // 3. Validate the DataSection against the provided strategy. - if (!ValidateDataSection(header, strategy, dataBody)) - { - return false; - } - - // 4. Validate that the CodeSections are non-empty and their sizes are valid. - CompoundSectionHeader codeSections = header.CodeSections; - if (!ValidateCodeSectionsNonZero(codeSections)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, CodeSection count ({codeSections.Count}) is zero or contains an empty section."); - return false; - } - - // The number of code sections should match the number of type entries, - // which is derived by dividing the TypeSection size by the minimum type section size. - int expectedTypeCount = header.TypeSection.Size / MINIMUM_TYPESECTION_SIZE; - if (codeSections.Count != expectedTypeCount) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, CodeSections count ({codeSections.Count}) does not match expected type count ({expectedTypeCount})."); - return false; - } - - // 5. Validate the content of the TypeSection. - ReadOnlySpan typeSectionBytes = container.Slice(header.TypeSection.Start, header.TypeSection.Size); - if (!ValidateTypeSection(typeSectionBytes)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Invalid TypeSection content."); - return false; - } - - return true; - } - - /// - /// Validates the DataSection of the container based on the validation strategy. - /// - /// - /// The header containing the DataSection metadata. - /// - /// - /// The validation strategy flags that determine the validation rules. - /// - /// - /// The slice of the container representing the data section. - /// - /// - /// true if the DataSection is valid; otherwise, false. - /// - private static bool ValidateDataSection(in EofHeader header, ValidationStrategy strategy, ReadOnlySpan dataBody) - { - // If full body validation is requested, or it is runtime mode (but not both) - // the DataSection size must be less than or equal to the data available. - if ((strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) ^ strategy.HasFlag(ValidationStrategy.ValidateFullBody)) - && header.DataSection.Size > dataBody.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, DataSection size ({header.DataSection.Size}) exceeds available data ({dataBody.Length})."); - return false; - } - - // When trailing bytes are not allowed, the DataSection cannot exceed the stated size data. - // Underflow cases were checked above as they don't apply in all cases - if (!strategy.HasFlag(ValidationStrategy.AllowTrailingBytes) - && strategy.HasFlag(ValidationStrategy.ValidateFullBody) - && header.DataSection.Size < dataBody.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, DataSection size ({header.DataSection.Size}) does not match available data ({dataBody.Length})."); - return false; - } - - return true; - } - - /// - /// Validates that the CodeSections are non-empty and that none of the subsection sizes are zero. - /// - /// - /// The compound header for the code sections. - /// - /// - /// true if the code sections are valid; otherwise, false. - /// - private static bool ValidateCodeSectionsNonZero(CompoundSectionHeader codeSections) => - // The code sections must contain at least one subsection and none may have a size of zero. - codeSections.Count > 0 && !codeSections.SubSectionsSizes.Any(size => size == 0); - - /// - /// Validates the instructions in all code sections of the provided EOF container. - /// - /// - /// The EOF container that holds the code sections to be validated. - /// - /// - /// The validation strategy to use when validating the instructions. - /// - /// - /// A instance tracking nested container processing, - /// which is used during instruction validation. - /// - /// - /// true if all code sections have been validated successfully; otherwise, false. - /// - private static bool ValidateCodeSections(in EofContainer eofContainer, ValidationStrategy strategy, in QueueManager containerQueue) - { - // Initialize a queue manager for the code sections. The queue capacity is set - // to the number of code sections in the container header. - QueueManager sectionQueue = new(eofContainer.Header.CodeSections.Count); - - // Enqueue the primary code section (index 0) with the given strategy. - sectionQueue.Enqueue(0, strategy); - - // Process each code section until the sectionQueue is empty. - while (sectionQueue.TryDequeue(out (int Index, ValidationStrategy Strategy) sectionIdx)) - { - // If this section has already been processed, skip it. - if (sectionQueue.VisitedContainers[sectionIdx.Index] != 0) - { - continue; - } - - // Validate the instructions in the current code section. - // This method call is responsible for checking the validity of the instructions - // within the code section at the given index. - if (!ValidateInstructions(eofContainer, sectionIdx.Index, strategy, in sectionQueue, in containerQueue)) - { - return false; - } - - // Mark the current code section as visited to avoid duplicate processing. - sectionQueue.MarkVisited(sectionIdx.Index, ValidationStrategy.Validate); - } - - // After processing, confirm that all expected code sections were visited. - return sectionQueue.IsAllVisitedAndNotAmbiguous(); - } - - /// - /// Validates the type section of an EOF container by verifying that the section header - /// and each individual type entry conform to the expected format and limits. - /// - /// - /// A read-only span of bytes representing the type section. - /// - /// - /// true if the type section is valid; otherwise, false. - /// - private static bool ValidateTypeSection(ReadOnlySpan types) - { - // The first type entry must have 0 inputs and a specific non-returning output indicator. - if (types[INPUTS_OFFSET] != 0 || types[OUTPUTS_OFFSET] != NON_RETURNING) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, first 2 bytes of type section must be 0s"); - return false; - } - - // The total length of the type section must be an integer multiple of the fixed entry size. - if (types.Length % MINIMUM_TYPESECTION_SIZE != 0) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, type section length must be a product of {MINIMUM_TYPESECTION_SIZE}"); - return false; - } - - // Process each type section entry. - for (var offset = 0; offset < types.Length; offset += MINIMUM_TYPESECTION_SIZE) - { - // Extract the current entry. - ReadOnlySpan entry = types.Slice(offset, MINIMUM_TYPESECTION_SIZE); - - // Validate the individual type entry. - if (!ValidateTypeSectionEntry(entry)) - return false; - } - - return true; - } - - /// - /// Validates an individual type section entry by checking the input count, output count, - /// and maximum stack height against defined limits. - /// - /// - /// A read-only span of bytes representing a single type section entry. - /// - /// - /// true if the entry is valid; otherwise, false. - /// - private static bool ValidateTypeSectionEntry(ReadOnlySpan entry) - { - // Retrieve the input and output counts from the fixed offsets. - byte inputCount = entry[INPUTS_OFFSET]; - byte outputCount = entry[OUTPUTS_OFFSET]; - - // Read the maximum stack height (a 16-bit value) from the designated slice. - ushort maxStackHeight = entry.Slice(MAX_STACK_HEIGHT_OFFSET, MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16(); - - // Validate that the input count does not exceed the allowed maximum. - if (inputCount > INPUTS_MAX) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Too many inputs: {inputCount}"); - return false; - } - - // Validate that the output count is within allowed limits. - // The exception is if the output count is set to NON_RETURNING. - if (outputCount > OUTPUTS_MAX && outputCount != NON_RETURNING) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Too many outputs: {outputCount}"); - return false; - } - - // Ensure the maximum stack height does not exceed the defined limit. - if (maxStackHeight > MAX_STACK_HEIGHT) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Stack depth too high: {maxStackHeight}"); - return false; - } - - return true; - } - /// - /// Validates the instructions of the given code section in an EOF container. - /// - /// The container holding the EOF bytecode and type sections. - /// The index of the code section to validate. - /// The validation strategy (e.g. runtime or InitCode mode). - /// A queue manager for additional code sections to be validated. - /// A queue manager for container sections that need validation. - /// True if the code section is valid; otherwise, false. - private static bool ValidateInstructions( - in EofContainer eofContainer, - int sectionId, - ValidationStrategy strategy, - in QueueManager sectionsWorklist, - in QueueManager containersWorklist) - { - ReadOnlySpan code = eofContainer.CodeSections[sectionId].Span; - - // A code section must contain at least one byte. - if (code.Length < 1) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, CodeSection {sectionId} is too short to be valid"); - return false; - } - - // Allocate temporary bitmaps for tracking jump destinations. - int bitmapLength = code.Length / BYTE_BIT_COUNT + 1; - byte[] invalidJmpLocationArray = ArrayPool.Shared.Rent(bitmapLength); - byte[] jumpDestinationsArray = ArrayPool.Shared.Rent(bitmapLength); - - try - { - // Ensure that we only work on the portion of the rented arrays that we need. - Span invalidJumpDestinations = invalidJmpLocationArray.AsSpan(0, bitmapLength); - Span jumpDestinations = jumpDestinationsArray.AsSpan(0, bitmapLength); - invalidJumpDestinations.Clear(); - jumpDestinations.Clear(); - - ReadOnlySpan currentTypeSection = eofContainer.TypeSections[sectionId].Span; - bool isCurrentSectionNonReturning = currentTypeSection[OUTPUTS_OFFSET] == NON_RETURNING; - // If the section is non–returning, an exit is already implied. - bool hasRequiredSectionExit = isCurrentSectionNonReturning; - - int position = 0; - Instruction opcode = Instruction.STOP; - - while (position < code.Length) - { - opcode = (Instruction)code[position]; - int nextPosition = position + 1; - - // Check for undefined opcodes in the EOF context. - if (!opcode.IsValid(isEofContext: true)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains undefined opcode {opcode}"); - return false; - } - else if (opcode is Instruction.RETURN or Instruction.STOP) - { - // RETURN/STOP are disallowed in InitCode mode. - if (strategy.HasFlag(ValidationStrategy.ValidateInitCodeMode)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); - return false; - } - else - { - // If the container has already been marked for InitCode mode, RETURN/STOP is not allowed. - if (containersWorklist.VisitedContainers[0] == ValidationStrategy.ValidateInitCodeMode) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); - return false; - } - else - { - containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateRuntimeMode; - } - } - } - else if (opcode == Instruction.RETURNCODE) - { - // Validate the RETURNCODE branch. - if (!ValidateReturnCode(ref nextPosition, strategy, containersWorklist, eofContainer, code, invalidJumpDestinations)) - return false; - } - else if (opcode is Instruction.RJUMP or Instruction.RJUMPI) - { - // Validate relative jump instructions. - if (!ValidateRelativeJump(ref nextPosition, opcode, code, jumpDestinations, invalidJumpDestinations)) - return false; - } - else if (opcode == Instruction.JUMPF) - { - if (nextPosition + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} Argument underflow"); - return false; - } - - ushort targetSectionId = code.Slice(nextPosition, TWO_BYTE_LENGTH).ReadEthUInt16(); - - if (targetSectionId >= eofContainer.Header.CodeSections.Count) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} to unknown code section"); - return false; - } - - ReadOnlySpan targetTypeSection = eofContainer.TypeSections[targetSectionId].Span; - byte targetSectionOutputCount = targetTypeSection[OUTPUTS_OFFSET]; - bool isTargetSectionNonReturning = targetTypeSection[OUTPUTS_OFFSET] == NON_RETURNING; - byte currentSectionOutputCount = currentTypeSection[OUTPUTS_OFFSET]; - - // Check that the jump target does not require more outputs than the current section. - if (!isTargetSectionNonReturning && currentSectionOutputCount < targetSectionOutputCount) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} to code section with more outputs"); - return false; - } - - // Non–returning sections must only jump to other non–returning sections. - if (isCurrentSectionNonReturning && !isTargetSectionNonReturning) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} from non-returning must target non-returning"); - return false; - } - - // JUMPF is only returning when the target is returning - if (!isTargetSectionNonReturning) - { - hasRequiredSectionExit = true; - } - - sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.FlagMultipleBits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); - } - else if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) - { - if (nextPosition + ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); - return false; - } - BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); - } - else if (opcode == Instruction.RJUMPV) - { - // Validate the relative jump table. - if (!ValidateRelativeJumpV(ref nextPosition, code, jumpDestinations, invalidJumpDestinations)) - return false; - } - else if (opcode == Instruction.CALLF) - { - // Validate the CALLF instruction. - if (!ValidateCallF(ref nextPosition, eofContainer, sectionsWorklist, strategy, code, invalidJumpDestinations)) - return false; - } - else if (opcode == Instruction.RETF) - { - // RETF indicates a proper exit from a section. Non–returning sections are not allowed to use RETF. - hasRequiredSectionExit = true; - if (isCurrentSectionNonReturning) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, non returning sections are not allowed to use opcode {Instruction.RETF}"); - return false; - } - } - else if (opcode == Instruction.DATALOADN) - { - // Validate that the data offset is within the data section bounds. - if (!ValidateDataLoadN(ref nextPosition, eofContainer, code, invalidJumpDestinations)) - return false; - } - else if (opcode == Instruction.EOFCREATE) - { - // Validate the EOFCREATE instruction. - if (!ValidateEofCreate(ref nextPosition, eofContainer, containersWorklist, code, invalidJumpDestinations)) - return false; - } - else if (opcode >= Instruction.PUSH0 && opcode <= Instruction.PUSH32) - { - int pushDataLength = opcode - Instruction.PUSH0; - if (nextPosition + pushDataLength > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} PC Reached out of bounds"); - return false; - } - BitmapHelper.FlagMultipleBits(pushDataLength, invalidJumpDestinations, ref nextPosition); - } - - position = nextPosition; - } - - if (position > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; - } - - if (!opcode.IsTerminating()) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code section {sectionId} ends with a non-terminating opcode"); - return false; - } - - if (!hasRequiredSectionExit) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code section {sectionId} is returning and does not have a RETF or JUMPF"); - return false; - } - - if (BitmapHelper.CheckCollision(invalidJumpDestinations, jumpDestinations)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination detected"); - return false; - } - - if (!ValidateStackState(sectionId, code, eofContainer.TypeSection.Span)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Invalid Stack state"); - return false; - } - return true; - } - finally - { - ArrayPool.Shared.Return(invalidJmpLocationArray); - ArrayPool.Shared.Return(jumpDestinationsArray); - } - } - - /// - /// Validates the RETURNCODE instruction branch. - /// This branch verifies that the container mode is switched properly and that the immediate argument is valid. - /// - /// A reference to the current position pointer (advanced on success). - /// The current validation strategy. - /// The container worklist queue. - /// The entire EOF container. - /// The entire code span. - /// The bitmap tracking invalid jump locations. - /// True if the RETURNCODE branch is valid; otherwise, false. - private static bool ValidateReturnCode( - ref int pos, - ValidationStrategy strategy, - in QueueManager containersWorklist, - in EofContainer eofContainer, - ReadOnlySpan code, - Span invalidJumpDestinations) - { - if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {Instruction.RETURNCODE} opcode"); - return false; - } - else - { - if (containersWorklist.VisitedContainers[0] == ValidationStrategy.ValidateRuntimeMode) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {Instruction.RETURNCODE} opcode"); - return false; - } - else - { - containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateInitCodeMode; - } - } - - // Ensure there is at least one byte for the immediate argument. - if (pos + ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCODE} Argument underflow"); - return false; - } - - ushort runtimeContainerId = code[pos]; - - if (eofContainer.Header.ContainerSections is null || runtimeContainerId >= eofContainer.Header.ContainerSections.Value.Count) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCODE}'s immediate argument must be less than containerSection.Count i.e.: {eofContainer.Header.ContainerSections?.Count}"); - return false; - } - - if (containersWorklist.VisitedContainers[runtimeContainerId + 1] != 0 && - containersWorklist.VisitedContainers[runtimeContainerId + 1] != ValidationStrategy.ValidateRuntimeMode) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCODE}'s target container can only be a runtime mode bytecode"); - return false; - } - - containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode | ValidationStrategy.ValidateFullBody); - BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref pos); - return true; - } - - /// - /// Validates a relative jump instruction (RJUMP or RJUMPI). - /// Verifies that the two-byte immediate exists and that the computed destination is within code bounds. - /// - /// A reference to the current position pointer (advanced on success). - /// The jump opcode being validated. - /// The entire code span. - /// The bitmap for valid jump destinations. - /// The bitmap for invalid jump destinations. - /// True if the relative jump is valid; otherwise, false. - private static bool ValidateRelativeJump( - ref int pos, - Instruction opcode, - ReadOnlySpan code, - Span jumpDestinations, - Span invalidJumpDestinations) - { - if (pos + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); - return false; - } - - short offset = code.Slice(pos, TWO_BYTE_LENGTH).ReadEthInt16(); - int rJumpDest = offset + TWO_BYTE_LENGTH + pos; - - if (rJumpDest < 0 || rJumpDest >= code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Destination outside of Code bounds"); - return false; - } - - BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, jumpDestinations, ref rJumpDest); - BitmapHelper.FlagMultipleBits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); - return true; - } - - /// - /// Validates the relative jump table instruction (RJUMPV). - /// Ensures that the jump table exists, has at least one entry, and that every computed destination is within bounds. - /// - /// A reference to the current position pointer (advanced on success). - /// The entire code span. - /// The bitmap for valid jump destinations. - /// The bitmap for invalid jump destinations. - /// True if the RJUMPV instruction is valid; otherwise, false. - private static bool ValidateRelativeJumpV( - ref int pos, - ReadOnlySpan code, - Span jumpDestinations, - Span invalidJumpDestinations) - { - if (pos + ONE_BYTE_LENGTH + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Argument underflow"); - return false; - } - - // The jump table length is encoded as immediate value + 1. - ushort count = (ushort)(code[pos] + 1); - if (count < MINIMUMS_ACCEPTABLE_JUMPV_JUMPTABLE_LENGTH) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumpTable must have at least 1 entry"); - return false; - } - - if (pos + ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumpTable underflow"); - return false; - } - - int immediateValueSize = ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH; - - for (int j = 0; j < count; j++) - { - short offset = code.Slice(pos + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthInt16(); - int rJumpDest = offset + immediateValueSize + pos; - if (rJumpDest < 0 || rJumpDest >= code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Destination outside of Code bounds"); - return false; - } - BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, jumpDestinations, ref rJumpDest); - } - - BitmapHelper.FlagMultipleBits(immediateValueSize, invalidJumpDestinations, ref pos); - return true; - } - - /// - /// Validates the CALLF instruction branch. - /// Checks that the target code section exists, that its type section indicates a returning function, - /// and that the jump table bits are handled appropriately. - /// - /// A reference to the current position pointer (advanced on success). - /// The EOF container containing code and type sections. - /// The queue manager for code sections. - /// The current validation strategy. - /// The entire code span. - /// The bitmap for invalid jump destinations. - /// True if the CALLF branch is valid; otherwise, false. - private static bool ValidateCallF( - ref int pos, - in EofContainer eofContainer, - in QueueManager sectionsWorklist, - ValidationStrategy strategy, - ReadOnlySpan code, - Span invalidJumpDestinations) - { - if (pos + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Argument underflow"); - return false; - } - - ushort targetSectionId = code.Slice(pos, TWO_BYTE_LENGTH).ReadEthUInt16(); - if (targetSectionId >= eofContainer.Header.CodeSections.Count) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Invalid Section Id"); - return false; - } - - ReadOnlySpan targetTypeSection = eofContainer.TypeSections[targetSectionId].Span; - byte targetSectionOutputCount = targetTypeSection[OUTPUTS_OFFSET]; - - if (targetSectionOutputCount == NON_RETURNING) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} into non-returning function"); - return false; - } - - sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.FlagMultipleBits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); - return true; - } - - /// - /// Validates the DATALOADN instruction branch. - /// Verifies that the two-byte immediate argument is within the bounds of the data section. - /// - /// A reference to the current position pointer (advanced on success). - /// The EOF container holding the data section. - /// The entire code span. - /// The bitmap for invalid jump destinations. - /// True if the DATALOADN branch is valid; otherwise, false. - private static bool ValidateDataLoadN( - ref int pos, - in EofContainer eofContainer, - ReadOnlySpan code, - Span invalidJumpDestinations) - { - if (pos + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.DATALOADN} Argument underflow"); - return false; - } - - ushort dataSectionOffset = code.Slice(pos, TWO_BYTE_LENGTH).ReadEthUInt16(); - if (dataSectionOffset + 32 > eofContainer.Header.DataSection.Size) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.DATALOADN}'s immediate argument must be less than dataSection.Length / 32 i.e.: {eofContainer.Header.DataSection.Size / 32}"); - return false; - } - - BitmapHelper.FlagMultipleBits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); - return true; - } - - /// - /// Validates the EOFCREATE instruction branch. - /// Ensures that the immediate argument is within the valid range for container sections - /// and that the target container is in the proper mode. - /// - /// A reference to the current position pointer (advanced on success). - /// The EOF container containing container sections. - /// The container worklist queue. - /// The entire code span. - /// The bitmap for invalid jump destinations. - /// True if the EOFCREATE branch is valid; otherwise, false. - private static bool ValidateEofCreate( - ref int pos, - in EofContainer eofContainer, - in QueueManager containersWorklist, - ReadOnlySpan code, - Span invalidJumpDestinations) - { - if (pos + ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE} Argument underflow"); - return false; - } - - int initCodeSectionId = code[pos]; - if (eofContainer.Header.ContainerSections is null || initCodeSectionId >= eofContainer.Header.ContainerSections.Value.Count) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must fall within the Containers' range available, i.e.: {eofContainer.Header.ContainerSections?.Count}"); - return false; - } - - if (containersWorklist.VisitedContainers[initCodeSectionId + 1] != 0 && - containersWorklist.VisitedContainers[initCodeSectionId + 1] != ValidationStrategy.ValidateInitCodeMode) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s target container can only be a initCode mode bytecode"); - return false; - } - - containersWorklist.Enqueue(initCodeSectionId + 1, ValidationStrategy.ValidateInitCodeMode | ValidationStrategy.ValidateFullBody); - BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref pos); - return true; - } - - /// - /// Validates the stack state for a given section of bytecode. - /// This method checks that the code’s instructions maintain a valid stack height - /// and that all control flow paths (calls, jumps, returns) yield a consistent stack state. - /// - /// The identifier for the section being validated. - /// The bytecode instructions to validate. - /// - /// A section of type metadata containing input/output stack requirements and maximum stack height constraints - /// for each section. - /// - /// True if the stack state is valid; otherwise, false. - public static bool ValidateStackState(int sectionId, ReadOnlySpan code, ReadOnlySpan typeSection) - { - // Rent an array to record the stack bounds at each code offset. - StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); - Array.Fill(recordedStackHeight, new StackBounds(min: 1023, max: -1)); - - try - { - // Get the suggested maximum stack height for this section. - ushort suggestedMaxHeight = typeSection - .Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH) - .ReadEthUInt16(); - - // Determine the output count for this section. A value of NON_RETURNING indicates non-returning. - ushort currentSectionOutputs = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == NON_RETURNING - ? (ushort)0 - : typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - - // The initial stack height is determined by the number of inputs. - short peakStackHeight = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - - int unreachedBytes = code.Length; - int programCounter = 0; - // Initialize the recorded stack bounds for the starting instruction. - recordedStackHeight[0] = new(peakStackHeight, peakStackHeight); - StackBounds currentStackBounds = recordedStackHeight[0]; - - while (programCounter < code.Length) - { - Instruction opcode = (Instruction)code[programCounter]; - (ushort inputCount, ushort outputCount, ushort immediateCount) = opcode.StackRequirements(); - - int posPostInstruction = programCounter + 1; - if (posPostInstruction > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; - } - - bool isTargetSectionNonReturning = false; - - // Apply opcode-specific modifications for opcodes that carry immediate data. - if (opcode is Instruction.CALLF or Instruction.JUMPF or Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) - { - try - { - InstructionModificationResult mod = ApplyOpcodeImmediateModifiers(opcode, posPostInstruction, currentSectionOutputs, immediateCount, currentStackBounds, code, typeSection); - inputCount = mod.InputCount; - outputCount = mod.OutputCount; - immediateCount = mod.ImmediateCount; - isTargetSectionNonReturning = mod.IsTargetSectionNonReturning; - } - catch (InvalidOperationException) - { - // The helper methods throw on validation errors. - return false; - } - } - - // Check for stack underflow. - if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputCount) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputCount} but found {currentStackBounds.Min}"); - return false; - } - - // For non-terminating instructions, adjust the current stack bounds. - if (!opcode.IsTerminating()) - { - short delta = (short)(outputCount - inputCount); - currentStackBounds = new((short)(currentStackBounds.Min + delta), (short)(currentStackBounds.Max + delta)); - } - peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); - - // Process control-flow opcodes. - if (opcode == Instruction.RETF) - { - if (!ValidateReturnInstruction(sectionId, currentStackBounds, typeSection)) - return false; - } - else if (opcode is Instruction.RJUMP or Instruction.RJUMPI) - { - if (!ValidateRelativeJumpInstruction(opcode, programCounter, posPostInstruction, immediateCount, currentStackBounds, recordedStackHeight, code)) - return false; - } - else if (opcode == Instruction.RJUMPV) - { - if (!ValidateRelativeJumpVInstruction(programCounter, posPostInstruction, currentStackBounds, recordedStackHeight, code, out immediateCount, out posPostInstruction)) - return false; - } - - unreachedBytes -= 1 + immediateCount; - programCounter += 1 + immediateCount; - - if (programCounter < code.Length) - { - // Propagate recorded stack bounds for subsequent instructions. - if (opcode.IsTerminating()) - { - ref StackBounds recordedBounds = ref recordedStackHeight[programCounter]; - if (recordedBounds.Max < 0) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, opcode not forward referenced, section {sectionId} pc {programCounter}"); - return false; - } - currentStackBounds = recordedBounds; - } - else - { - ref StackBounds recordedBounds = ref recordedStackHeight[programCounter]; - recordedBounds.Combine(currentStackBounds); - currentStackBounds = recordedBounds; - } - } - } - - if (unreachedBytes != 0) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, bytecode has unreachable segments"); - return false; - } - - if (peakStackHeight != suggestedMaxHeight) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); - return false; - } - - if (peakStackHeight >= MAX_STACK_HEIGHT) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); - return false; - } - - return true; - } - finally - { - ArrayPool.Shared.Return(recordedStackHeight); - } - } - - /// - /// Holds the result from adjusting an opcode’s immediate data. - /// - private struct InstructionModificationResult - { - public ushort InputCount; - public ushort OutputCount; - public ushort ImmediateCount; - public bool IsTargetSectionNonReturning; - } - - /// - /// Adjusts the stack requirements for opcodes that carry immediate data. - /// For CALLF and JUMPF the target section information is read from the immediate bytes, - /// and additional validation is performed. - /// For DUPN, SWAPN, and EXCHANGE the immediate value adjusts the input/output counts. - /// - /// The current opcode. - /// The code offset immediately after the opcode. - /// The output count for the current section. - /// The base immediate count from the opcode’s stack requirements. - /// The current stack bounds. - /// The full bytecode. - /// The type section metadata. - /// A structure with the adjusted stack counts and immediate count. - /// Thrown if validation fails. - private static InstructionModificationResult ApplyOpcodeImmediateModifiers( - Instruction opcode, - int posPostInstruction, - ushort currentSectionOutputs, - ushort immediateCount, - in StackBounds currentStackBounds, - ReadOnlySpan code, - ReadOnlySpan typeSection) - { - var result = new InstructionModificationResult { ImmediateCount = immediateCount, IsTargetSectionNonReturning = false }; - - switch (opcode) - { - case Instruction.CALLF: - case Instruction.JUMPF: - { - // Read the target section identifier from the immediate bytes. - ushort targetSectionId = code.Slice(posPostInstruction, immediateCount).ReadEthUInt16(); - result.InputCount = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - result.OutputCount = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - result.IsTargetSectionNonReturning = result.OutputCount == NON_RETURNING; - if (result.IsTargetSectionNonReturning) - { - result.OutputCount = 0; - } - // Retrieve the maximum stack height allowed for the target section. - int targetMaxStackHeight = typeSection - .Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH) - .ReadEthUInt16(); - // Validate the stack height against the global maximum. - if (MAX_STACK_HEIGHT - targetMaxStackHeight + result.InputCount < currentStackBounds.Max) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, stack head during callF must not exceed {MAX_STACK_HEIGHT}"); - throw new InvalidOperationException("Stack height exceeded in CALLF/JUMPF"); - } - // For JUMPF (when returning) the stack state must match expected values. - if (opcode == Instruction.JUMPF && !result.IsTargetSectionNonReturning && - !(currentSectionOutputs + result.InputCount - result.OutputCount == currentStackBounds.Min && currentStackBounds.BoundsEqual())) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currentSectionOutputs + result.InputCount - result.OutputCount} but found {currentStackBounds.Max}"); - throw new InvalidOperationException("Invalid stack state in JUMPF"); - } - break; - } - case Instruction.DUPN: - { - int imm_n = 1 + code[posPostInstruction]; - result.InputCount = (ushort)imm_n; - result.OutputCount = (ushort)(result.InputCount + 1); - break; - } - case Instruction.SWAPN: - { - int imm_n = 1 + code[posPostInstruction]; - result.InputCount = result.OutputCount = (ushort)(1 + imm_n); - break; - } - case Instruction.EXCHANGE: - { - int imm_n = 1 + (code[posPostInstruction] >> 4); - int imm_m = 1 + (code[posPostInstruction] & 0x0F); - result.InputCount = result.OutputCount = (ushort)(imm_n + imm_m + 1); - break; - } - default: - throw new NotSupportedException("Opcode does not require immediate modifier adjustments."); - } - - return result; - } - - /// - /// Validates the RETF instruction by checking that the current stack state exactly matches - /// the expected output count for the section. - /// - /// The identifier of the current section. - /// The current stack bounds. - /// The type section metadata. - /// True if the RETF instruction’s requirements are met; otherwise, false. - private static bool ValidateReturnInstruction(int sectionId, in StackBounds currentStackBounds, ReadOnlySpan typeSection) - { - int expectedHeight = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - if (expectedHeight != currentStackBounds.Min || !currentStackBounds.BoundsEqual()) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid required height {expectedHeight} but found {currentStackBounds.Min}"); - return false; - } - return true; - } - - /// - /// Validates relative jump instructions (RJUMP and RJUMPI). - /// Reads the jump offset, computes the destination and updates the recorded stack state as needed. - /// - /// The current opcode (RJUMP or RJUMPI). - /// The current program counter. - /// The offset immediately after the opcode. - /// The immediate count associated with the opcode. - /// The current stack bounds. - /// The array of recorded stack bounds per code offset. - /// The full bytecode. - /// True if the jump destination’s stack state is valid; otherwise, false. - private static bool ValidateRelativeJumpInstruction( - Instruction opcode, - int programCounter, - int posPostInstruction, - ushort immediateCount, - in StackBounds currentStackBounds, - StackBounds[] recordedStackHeight, - ReadOnlySpan code) - { - // Read the jump offset from the immediate bytes. - short offset = code.Slice(programCounter + 1, immediateCount).ReadEthInt16(); - int jumpDestination = posPostInstruction + immediateCount + offset; - - // For RJUMPI, record the current stack state after the immediate data. - if (opcode == Instruction.RJUMPI && (posPostInstruction + immediateCount < recordedStackHeight.Length)) - recordedStackHeight[posPostInstruction + immediateCount].Combine(currentStackBounds); - - return ValidateJumpDestination(jumpDestination, programCounter, currentStackBounds, recordedStackHeight); - } - - /// - /// Validates the RJUMPV instruction (relative jump vector). - /// Reads the jump vector, validates each jump destination and returns updated immediate count and position. - /// - /// The current program counter. - /// The offset immediately after the opcode. - /// The current stack bounds. - /// The array of recorded stack bounds per code offset. - /// The full bytecode. - /// The updated immediate count after processing the jump vector. - /// The updated position after the jump vector. - /// True if all jump destinations in the vector are valid; otherwise, false. - private static bool ValidateRelativeJumpVInstruction( - int programCounter, - int posPostInstruction, - in StackBounds currentStackBounds, - StackBounds[] recordedStackHeight, - ReadOnlySpan code, - out ushort updatedImmediateCount, - out int updatedPosPostInstruction) - { - int count = code[posPostInstruction] + 1; - updatedImmediateCount = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); - - // Validate each jump destination in the jump vector. - for (short j = 0; j < count; j++) - { - int casePosition = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; - int offset = code.Slice(casePosition, TWO_BYTE_LENGTH).ReadEthInt16(); - int jumpDestination = posPostInstruction + updatedImmediateCount + offset; - if (!ValidateJumpDestination(jumpDestination, programCounter, currentStackBounds, recordedStackHeight)) - { - updatedPosPostInstruction = posPostInstruction; - return false; - } - } - - updatedPosPostInstruction = posPostInstruction + updatedImmediateCount; - if (updatedPosPostInstruction > code.Length) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; - } - return true; - } - - - /// - /// Validates the recorded stack bounds at a given jump destination. - /// For forward jumps the current stack state is combined with the destination’s state; - /// for backward jumps the destination’s recorded state must exactly match the current state. - /// - /// The target code offset for the jump. - /// The current program counter. - /// The current stack bounds. - /// The array of recorded stack bounds per code offset. - /// True if the destination’s stack state is valid; otherwise, false. - private static bool ValidateJumpDestination( - int jumpDestination, - int programCounter, - in StackBounds currentStackBounds, - StackBounds[] recordedStackHeight) - { - ref StackBounds recordedBounds = ref recordedStackHeight[jumpDestination]; - if (jumpDestination > programCounter) - { - recordedBounds.Combine(currentStackBounds); - } - else if (recordedBounds != currentStackBounds) - { - if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); - return false; - } - return true; - } - - [StructLayout(LayoutKind.Auto)] - private readonly struct QueueManager(int containerCount) - { - public readonly Queue<(int index, ValidationStrategy strategy)> ContainerQueue = new(); - public readonly ValidationStrategy[] VisitedContainers = new ValidationStrategy[containerCount]; - - public void Enqueue(int index, ValidationStrategy strategy) => ContainerQueue.Enqueue((index, strategy)); - - public void MarkVisited(int index, ValidationStrategy strategy) => VisitedContainers[index] |= strategy; - - public bool TryDequeue(out (int Index, ValidationStrategy Strategy) worklet) => ContainerQueue.TryDequeue(out worklet); - - public bool IsAllVisitedAndNotAmbiguous() => VisitedContainers.All(validation => - { - bool isEofCreate = validation.HasFlag(ValidationStrategy.InitCodeMode); - bool isReturnCode = validation.HasFlag(ValidationStrategy.RuntimeMode); - - // Should be referenced but not by both EofCreate and ReturnCode. - return validation != 0 && !(isEofCreate && isReturnCode); - }); - } - - [StructLayout(LayoutKind.Auto)] - private ref struct Sizes - { - public ushort? TypeSectionSize; - public ushort? CodeSectionSize; - public ushort? DataSectionSize; - public ushort? ContainerSectionSize; - } - - internal enum Separator : byte - { - KIND_TYPE = 0x01, - KIND_CODE = 0x02, - KIND_CONTAINER = 0x03, - KIND_DATA = 0x04, - TERMINATOR = 0x00 - } -} - -[StructLayout(LayoutKind.Auto)] -internal readonly struct StackBounds(short min, short max) -{ - public readonly short Min = min; - public readonly short Max = max; - - public readonly bool BoundsEqual() => Max == Min; - - public static bool operator ==(in StackBounds left, in StackBounds right) => left.Max == right.Max && right.Min == left.Min; - public static bool operator !=(in StackBounds left, in StackBounds right) => !(left == right); - public override readonly bool Equals(object obj) => obj is StackBounds bounds && this == bounds; - public override readonly int GetHashCode() => (Max << 16) | (int)Min; -} - -file static class StackBoundsExtensions -{ - public static void Combine(this ref StackBounds bounds, StackBounds other) - { - bounds = new(Math.Min(bounds.Min, other.Min), Math.Max(bounds.Max, other.Max)); - } -} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs deleted file mode 100644 index 70b20f0089d6..000000000000 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Nethermind.Evm.EvmObjectFormat; - -interface IEofVersionHandler -{ - bool TryParseEofHeader(ReadOnlyMemory code, ValidationStrategy strategy, [NotNullWhen(true)] out EofHeader? header); - bool TryGetEofContainer(ValidationStrategy strategy, [NotNullWhen(true)] out EofContainer? header, ReadOnlyMemory code); -} diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 95461216d89a..74e6ada47d3a 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -25,7 +25,6 @@ public ref struct EvmStack { public const int RegisterLength = 1; public const int MaxStackSize = 1025; - public const int ReturnStackSize = 1025; public const int WordSize = 32; public const int AddressSize = 20; diff --git a/src/Nethermind/Nethermind.Evm/ExecutionType.cs b/src/Nethermind/Nethermind.Evm/ExecutionType.cs index 91642266e027..83a94d5b0648 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionType.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionType.cs @@ -8,29 +8,14 @@ namespace Nethermind.Evm { public static class ExecutionTypeExtensions { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAnyCreateLegacy(this ExecutionType executionType) => - executionType is ExecutionType.CREATE or ExecutionType.CREATE2; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAnyCreateEof(this ExecutionType executionType) => - executionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE; - // did not want to use flags here specifically [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAnyCreate(this ExecutionType executionType) => - IsAnyCreateLegacy(executionType) || IsAnyCreateEof(executionType); + executionType is ExecutionType.CREATE or ExecutionType.CREATE2; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAnyCall(this ExecutionType executionType) => - IsAnyCallLegacy(executionType) || IsAnyCallEof(executionType); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAnyCallLegacy(this ExecutionType executionType) => executionType is ExecutionType.CALL or ExecutionType.STATICCALL or ExecutionType.DELEGATECALL or ExecutionType.CALLCODE; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAnyCallEof(this ExecutionType executionType) => - executionType is ExecutionType.EOFCALL or ExecutionType.EOFSTATICCALL or ExecutionType.EOFDELEGATECALL; - public static Instruction ToInstruction(this ExecutionType executionType) => executionType switch { @@ -41,10 +26,6 @@ public static Instruction ToInstruction(this ExecutionType executionType) => ExecutionType.DELEGATECALL => Instruction.DELEGATECALL, ExecutionType.CREATE => Instruction.CREATE, ExecutionType.CREATE2 => Instruction.CREATE2, - ExecutionType.EOFCREATE => Instruction.EOFCREATE, - ExecutionType.EOFCALL => Instruction.EXTCALL, - ExecutionType.EOFSTATICCALL => Instruction.EXTSTATICCALL, - ExecutionType.EOFDELEGATECALL => Instruction.EXTDELEGATECALL, _ => throw new NotSupportedException($"Execution type {executionType} is not supported.") }; } @@ -59,11 +40,6 @@ public enum ExecutionType : byte CALLCODE, CREATE, CREATE2, - EOFCREATE, - TXCREATE, - EOFCALL, - EOFSTATICCALL, - EOFDELEGATECALL, } // ReSharper restore IdentifierTypo InconsistentNaming } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 343bb0380996..311088cf5987 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -2,13 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using FastEnumUtility; -using Nethermind.Core.Specs; -using Nethermind.Specs.Forks; namespace Nethermind.Evm; @@ -166,243 +160,18 @@ public enum Instruction : byte LOG3 = 0xa3, LOG4 = 0xa4, - DATALOAD = 0xd0, - DATALOADN = 0xd1, - DATASIZE = 0xd2, - DATACOPY = 0xd3, - - RJUMP = 0xe0, - RJUMPI = 0xe1, - RJUMPV = 0xe2, - CALLF = 0xe3, - RETF = 0xe4, - JUMPF = 0xe5, DUPN = 0xe6, SWAPN = 0xe7, EXCHANGE = 0xe8, - EOFCREATE = 0xec, - - RETURNCODE = 0xee, - CREATE = 0xf0, CALL = 0xf1, CALLCODE = 0xf2, RETURN = 0xf3, DELEGATECALL = 0xf4, CREATE2 = 0xf5, - RETURNDATALOAD = 0xf7, - EXTCALL = 0xf8, - EXTDELEGATECALL = 0xf9, STATICCALL = 0xfa, - EXTSTATICCALL = 0xfb, REVERT = 0xfd, INVALID = 0xfe, SELFDESTRUCT = 0xff } - -public static class InstructionExtensions -{ - private readonly static bool[] _terminatingInstructions = CreateTerminatingInstructionsLookup(); - private readonly static bool[] _validLegacyInstructions = CreateValidInstructionsLookup(isEofContext: false); - private readonly static bool[] _validEofInstructions = CreateValidInstructionsLookup(isEofContext: true); - - public static bool IsTerminating(this Instruction instruction) - => Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_terminatingInstructions), (int)instruction); - - public static bool IsValid(this Instruction instruction, bool isEofContext) - => Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(isEofContext ? _validEofInstructions : _validLegacyInstructions), (int)instruction); - - public static (ushort InputCount, ushort OutputCount, ushort immediates) StackRequirements(this Instruction instruction) - { - switch (instruction) - { - case Instruction.STOP: - case Instruction.INVALID: - case Instruction.JUMPDEST: - case Instruction.RETF: - return (0, 0, 0); - case Instruction.POP: - case Instruction.SELFDESTRUCT: - case Instruction.JUMP: - return (1, 0, 0); - case Instruction.ISZERO: - case Instruction.NOT: - case Instruction.BALANCE: - case Instruction.CALLDATALOAD: - case Instruction.EXTCODESIZE: - case Instruction.RETURNDATALOAD: - case Instruction.EXTCODEHASH: - case Instruction.BLOCKHASH: - case Instruction.MLOAD: - case Instruction.SLOAD: - case Instruction.BLOBHASH: - case Instruction.TLOAD: - case Instruction.DATALOAD: - return (1, 1, 0); - case Instruction.MSTORE: - case Instruction.MSTORE8: - case Instruction.SSTORE: - case Instruction.LOG0: - case Instruction.REVERT: - case Instruction.TSTORE: - case Instruction.RETURN: - case Instruction.JUMPI: - return (2, 0, 0); - case Instruction.RETURNCODE: - return (2, 2, 1); - case Instruction.CALLDATACOPY: - case Instruction.CODECOPY: - case Instruction.RETURNDATACOPY: - case Instruction.LOG1: - case Instruction.DATACOPY: - case Instruction.MCOPY: - return (3, 0, 0); - case Instruction.EXTCODECOPY: - case Instruction.LOG2: - return (4, 0, 0); - case Instruction.LOG3: - return (5, 0, 0); - case Instruction.LOG4: - return (6, 0, 0); - case Instruction.ADDMOD: - case Instruction.MULMOD: - case Instruction.CREATE: - case Instruction.EXTSTATICCALL: - case Instruction.EXTDELEGATECALL: - return (3, 1, 0); - case Instruction.CREATE2: - case Instruction.EXTCALL: - return (4, 1, 0); - case Instruction.EOFCREATE: - return (4, 1, 1); - case Instruction.ADDRESS: - case Instruction.ORIGIN: - case Instruction.CALLER: - case Instruction.CALLVALUE: - case Instruction.CALLDATASIZE: - case Instruction.CODESIZE: - case Instruction.GASPRICE: - case Instruction.RETURNDATASIZE: - case Instruction.COINBASE: - case Instruction.TIMESTAMP: - case Instruction.NUMBER: - case Instruction.PREVRANDAO: - case Instruction.GASLIMIT: - case Instruction.CHAINID: - case Instruction.SELFBALANCE: - case Instruction.BASEFEE: - case Instruction.MSIZE: - case Instruction.GAS: - case Instruction.PC: - case Instruction.BLOBBASEFEE: - case Instruction.DATASIZE: - case Instruction.SLOTNUM: - return (0, 1, 0); - case Instruction.RJUMP: - case Instruction.CALLF: - case Instruction.JUMPF: - return (0, 0, 2); - case Instruction.DATALOADN: - return (0, 1, 2); - case Instruction.RJUMPI: - return (1, 0, 2); - case Instruction.CALL: - case Instruction.DELEGATECALL: - case Instruction.STATICCALL: - case Instruction.CALLCODE: - return (6, 1, 0); - case >= Instruction.PUSH0 and <= Instruction.PUSH32: - return (0, 1, instruction - Instruction.PUSH0); - case >= Instruction.DUP1 and <= Instruction.DUP16: - return ((ushort)(instruction - Instruction.DUP1 + 1), (ushort)(instruction - Instruction.DUP1 + 2), 0); - case >= Instruction.SWAP1 and <= Instruction.SWAP16: - return ((ushort)(instruction - Instruction.SWAP1 + 2), (ushort)(instruction - Instruction.SWAP1 + 2), 0); - case Instruction.RJUMPV: - // multi-bytes opcode - return (1, 0, 0); - // multi-bytes opcode - case Instruction.SWAPN: - case Instruction.DUPN: - case Instruction.EXCHANGE: - return (0, 0, 1); - default: - return Enum.IsDefined(instruction) ? ((ushort)2, (ushort)1, (ushort)0) : ThrowNotImplemented(instruction); - } - } - - [DoesNotReturn, StackTraceHidden] - private static (ushort InputCount, ushort OutputCount, ushort immediates) ThrowNotImplemented(Instruction instruction) - => throw new NotImplementedException($"opcode {instruction} not implemented yet"); - - private static bool[] CreateValidInstructionsLookup(bool isEofContext) - { - bool[] instructions = new bool[byte.MaxValue]; - for (int i = 0; i < instructions.Length; i++) - { - instructions[i] = IsValidInstruction((Instruction)i, isEofContext); - } - return instructions; - } - - private static bool[] CreateTerminatingInstructionsLookup() - { - bool[] instructions = new bool[byte.MaxValue]; - - instructions[(int)Instruction.STOP] = true; - instructions[(int)Instruction.RJUMP] = true; - instructions[(int)Instruction.RETF] = true; - instructions[(int)Instruction.JUMPF] = true; - instructions[(int)Instruction.RETURNCODE] = true; - instructions[(int)Instruction.RETURN] = true; - instructions[(int)Instruction.REVERT] = true; - instructions[(int)Instruction.INVALID] = true; - - return instructions; - } - - private static bool IsValidInstruction(Instruction instruction, bool isEofContext) - { - if (!Enum.IsDefined(instruction)) - { - return false; - } - - return instruction switch - { - Instruction.CALLF or Instruction.RETF or Instruction.JUMPF => isEofContext, - Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE => isEofContext, - Instruction.RJUMP or Instruction.RJUMPI or Instruction.RJUMPV => isEofContext, - Instruction.RETURNCODE or Instruction.EOFCREATE => isEofContext, - Instruction.DATACOPY or Instruction.DATASIZE or Instruction.DATALOAD or Instruction.DATALOADN => isEofContext, - Instruction.EXTSTATICCALL or Instruction.EXTDELEGATECALL or Instruction.EXTCALL => isEofContext, - Instruction.RETURNDATALOAD => isEofContext, - Instruction.CALL => !isEofContext, - Instruction.CALLCODE => !isEofContext, - Instruction.DELEGATECALL => !isEofContext, - Instruction.STATICCALL => !isEofContext, - Instruction.SELFDESTRUCT => !isEofContext, - Instruction.JUMP => !isEofContext, - Instruction.JUMPI => !isEofContext, - Instruction.PC => !isEofContext, - Instruction.CREATE2 or Instruction.CREATE => !isEofContext, - Instruction.CODECOPY => !isEofContext, - Instruction.CODESIZE => !isEofContext, - Instruction.EXTCODEHASH => !isEofContext, - Instruction.EXTCODECOPY => !isEofContext, - Instruction.EXTCODESIZE => !isEofContext, - Instruction.GAS => !isEofContext, - _ => true - }; - } - - public static string? GetName(this Instruction instruction, IReleaseSpec? spec = null) - { - spec ??= Frontier.Instance; - return instruction switch - { - Instruction.JUMPDEST => spec.IsEofEnabled ? "NOP" : "JUMPDEST", - _ => FastEnum.IsDefined(instruction) ? FastEnum.GetName(instruction) : null, - }; - } -} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index fd4e4fd96db2..974f7984139c 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -360,12 +360,6 @@ public static EvmExceptionType InstructionReturn(VirtualMachine { - // RETURN is not allowed during contract creation. - if (vm.VmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) - { - goto BadInstruction; - } - // Pop memory position and length for the return data. if (!stack.PopUInt256(out UInt256 position) || !stack.PopUInt256(out UInt256 length)) @@ -386,7 +380,5 @@ public static EvmExceptionType InstructionReturn(VirtualMachine GetCode(VirtualMachine vm) => vm.VmState.Env.CodeInfo.CodeSpan; } + /// + /// Copies data from the previous call's return buffer into memory. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionReturnDataCopy( + VirtualMachine vm, + ref EvmStack stack, + ref TGasPolicy gas, + ref int programCounter) + where TGasPolicy : struct, IGasPolicy + where TTracingInst : struct, IFlag + { + if (!stack.PopUInt256(out UInt256 destOffset) || + !stack.PopUInt256(out UInt256 sourceOffset) || + !stack.PopUInt256(out UInt256 size)) + goto StackUnderflow; + + TGasPolicy.ConsumeDataCopyGas(ref gas, isExternalCode: false, GasCostOf.VeryLow, GasCostOf.Memory * EvmCalculations.Div32Ceiling(in size, out bool outOfGas)); + if (outOfGas) goto OutOfGas; + + ReadOnlyMemory returnDataBuffer = vm.ReturnDataBuffer; + if (UInt256.AddOverflow(size, sourceOffset, out UInt256 result) || result > returnDataBuffer.Length) + goto AccessViolation; + + if (!size.IsZero) + { + if (!TGasPolicy.UpdateMemoryCost(ref gas, in destOffset, size, vm.VmState)) + goto OutOfGas; + + ZeroPaddedSpan slice = returnDataBuffer.Span.SliceWithZeroPadding(sourceOffset, (int)size); + if (!vm.VmState.Memory.TrySave(in destOffset, in slice)) goto OutOfGas; + + if (TTracingInst.IsActive) + { + vm.TxTracer.ReportMemoryChange(destOffset, in slice); + } + } + + return EvmExceptionType.None; + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + AccessViolation: + return EvmExceptionType.AccessViolation; + } + /// /// Copies external code (from another account) into memory. /// Pops an address and three parameters (destination offset, source offset, and length) from the stack. @@ -182,12 +228,6 @@ public static EvmExceptionType InstructionExtCodeCopy( goto OutOfGas; } - // If EOF is enabled and the code is an EOF contract, use a predefined magic value. - if (spec.IsEofEnabled && EofValidator.IsEof(externalCode, out _)) - { - externalCode = EofValidator.MAGIC; - } - // Slice the external code starting at the source offset with appropriate zero-padding. ZeroPaddedSpan slice = externalCode.SliceWithZeroPadding(in b, (int)result); // Save the slice into memory at the destination offset. @@ -306,16 +346,7 @@ public static EvmExceptionType InstructionExtCodeSize( ReadOnlySpan accountCode = vm.CodeInfoRepository .GetCachedCodeInfo(address, followDelegation: false, spec, out _) .CodeSpan; - // If EOF is enabled and the code is an EOF contract, push a fixed size (2). - if (spec.IsEofEnabled && EofValidator.IsEof(accountCode, out _)) - { - stack.PushUInt32(2); - } - else - { - // Otherwise, push the actual code length. - stack.PushUInt32((uint)accountCode.Length); - } + stack.PushUInt32((uint)accountCode.Length); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor. OutOfGas: diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs index bd23b925c4e0..1c27b19a9b55 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs @@ -148,20 +148,11 @@ private static bool TestJumpCondition(ref EvmStack stack, out bool isOverflow) /// /// Stops the execution of the EVM. - /// In EOFCREATE or TXCREATE executions, the STOP opcode is considered illegal. /// [SkipLocalsInit] public static EvmExceptionType InstructionStop(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) where TGasPolicy : struct, IGasPolicy - { - // In contract creation contexts, a STOP is not permitted. - if (vm.VmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) - { - return EvmExceptionType.BadInstruction; - } - - return EvmExceptionType.Stop; - } + => EvmExceptionType.Stop; /// /// Implements the REVERT opcode. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 0945564dc811..7c7c24d3b3a3 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -6,7 +6,6 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.CodeAnalysis; -using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.GasPolicy; using Nethermind.Int256; using Nethermind.Evm.State; @@ -198,21 +197,11 @@ public static EvmExceptionType InstructionCreate(); - stack.PushZero(); - TGasPolicy.UpdateGasUp(ref gas, callGas); - goto None; - } - // Increment the nonce of the executing account to reflect the contract creation. state.IncrementNonce(env.ExecutingAccount); // Analyze and compile the initialization code. - CodeInfoFactory.CreateInitCodeInfo(initCode, spec, out CodeInfo? codeInfo, out _); + CodeInfo? codeInfo = CodeInfoFactory.CreateCodeInfo(initCode); // Take a snapshot of the current state. This allows the state to be reverted if contract creation fails. Snapshot snapshot = state.TakeSnapshot(); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index 9a29d8fc79f5..36f489171bb2 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -6,7 +6,6 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Core.Crypto; -using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.GasPolicy; using Nethermind.Evm.State; using static Nethermind.Evm.VirtualMachineStatics; @@ -379,6 +378,19 @@ public static uint Operation(VmState vmState) => (uint)vmState.Env.CodeInfo.CodeSpan.Length; } + /// + /// Retrieves the length of the return data buffer and pushes it onto the stack. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionReturnDataSize(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) + where TGasPolicy : struct, IGasPolicy + where TTracingInst : struct, IFlag + { + TGasPolicy.Consume(ref gas, GasCostOf.Base); + stack.PushUInt32((uint)vm.ReturnDataBuffer.Length); + return EvmExceptionType.None; + } + /// /// Returns the timestamp of the current block. /// @@ -643,60 +655,6 @@ public static EvmExceptionType InstructionExtCodeHash( return EvmExceptionType.StackUnderflow; } - /// - /// Retrieves the code hash of an external account, considering the possibility of an EOF-validated contract. - /// If the code is an EOF contract, a predefined EOF hash is pushed. - /// - /// The gas policy used for gas accounting. - /// The virtual machine instance. - /// The execution stack where the gas value will be pushed. - /// Reference to the gas state, updated by the operation's cost. - /// The current program counter. - /// - /// if gas is available, - /// if the gas becomes negative - /// or if not enough items on stack. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - IReleaseSpec spec = vm.Spec; - TGasPolicy.Consume(ref gas, spec.GasCosts.ExtCodeHashCost); - - Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; - if (!TGasPolicy.ConsumeAccountAccessGas(ref gas, spec, in vm.VmState.AccessTracker, vm.TxTracer.IsTracingAccess, address)) goto OutOfGas; - - IWorldState state = vm.WorldState; - if (state.IsDeadAccount(address)) - { - stack.PushZero(); - } - else - { - Memory code = state.GetCode(address); - // If the code passes EOF validation, push the EOF-specific hash. - if (EofValidator.IsEof(code, out _)) - { - stack.PushBytes(EofHash256); - } - else - { - // Otherwise, push the standard code hash. - stack.PushBytes(state.GetCodeHash(address).Bytes); - } - } - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } - /// /// Implements the PREVRANDAO opcode. /// Pushes the previous random value (post-merge) or block difficulty (pre-merge) onto the stack. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs deleted file mode 100644 index 68e23a59f613..000000000000 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ /dev/null @@ -1,1045 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Runtime.CompilerServices; -using Nethermind.Core; -using Nethermind.Core.Extensions; -using Nethermind.Core.Specs; -using Nethermind.Evm.CodeAnalysis; -using Nethermind.Evm.EvmObjectFormat; -using Nethermind.Evm.EvmObjectFormat.Handlers; -using Nethermind.Evm.GasPolicy; -using Nethermind.Evm.Tracing; -using Nethermind.Evm.State; -using static Nethermind.Evm.VirtualMachineStatics; - -namespace Nethermind.Evm; - -using Int256; - -internal static partial class EvmInstructions -{ - /// - /// Interface defining properties for an EOF call instruction. - /// - public interface IOpEofCall - { - // Indicates whether the call must be static. - virtual static bool IsStatic => false; - // Specifies the execution type of the call. - abstract static ExecutionType ExecutionType { get; } - } - - /// - /// Represents a standard EOF call instruction. - /// - public struct OpEofCall : IOpEofCall - { - public static ExecutionType ExecutionType => ExecutionType.EOFCALL; - } - - /// - /// Represents an EOF delegate call instruction. - /// - public struct OpEofDelegateCall : IOpEofCall - { - public static ExecutionType ExecutionType => ExecutionType.EOFDELEGATECALL; - } - - /// - /// Represents an EOF static call instruction. - /// - public struct OpEofStaticCall : IOpEofCall - { - public static bool IsStatic => true; - public static ExecutionType ExecutionType => ExecutionType.EOFSTATICCALL; - } - - /// - /// Retrieves the length of the return data buffer and pushes it onto the stack. - /// Deducts the base gas cost from the available gas. - /// - /// The gas policy used for gas accounting. - /// Tracing flag type. - /// The current virtual machine instance. - /// Reference to the operand stack. - /// Reference to the gas state. - /// Reference to the current program counter. - /// An indicating the outcome. - [SkipLocalsInit] - public static EvmExceptionType InstructionReturnDataSize(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - // Deduct base gas cost for this instruction. - TGasPolicy.Consume(ref gas, GasCostOf.Base); - - // Push the length of the return data buffer (as a 32-bit unsigned integer) onto the stack. - stack.PushUInt32((uint)vm.ReturnDataBuffer.Length); - - return EvmExceptionType.None; - } - - /// - /// Copies a slice from the VM's return data buffer into memory. - /// Parameters are popped from the stack (destination offset, source offset, and size). - /// Performs gas and memory expansion cost updates before copying. - /// - /// - /// A tracing flag type to conditionally report memory changes. - /// - /// The current virtual machine instance. - /// Reference to the operand stack. - /// Reference to the available gas. - /// Reference to the current program counter. - /// An representing success or the type of failure. - [SkipLocalsInit] - public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - // Pop the required parameters: destination memory offset, source offset in return data, and number of bytes to copy. - if (!stack.PopUInt256(out UInt256 destOffset) || - !stack.PopUInt256(out UInt256 sourceOffset) || - !stack.PopUInt256(out UInt256 size)) - { - goto StackUnderflow; - } - - // Deduct the fixed gas cost and the memory cost based on the size (rounded up to 32-byte words). - TGasPolicy.Consume(ref gas, GasCostOf.VeryLow + GasCostOf.Memory * EvmCalculations.Div32Ceiling(in size, out bool outOfGas)); - if (outOfGas) goto OutOfGas; - - ReadOnlyMemory returnDataBuffer = vm.ReturnDataBuffer; - // For legacy (non-EOF) code, ensure that the copy does not exceed the available return data. - if (vm.VmState.Env.CodeInfo.Version == 0 && - (UInt256.AddOverflow(size, sourceOffset, out UInt256 result) || result > returnDataBuffer.Length)) - { - goto AccessViolation; - } - - // Only perform the copy if size is non-zero. - if (!size.IsZero) - { - // Update memory cost for expanding memory to accommodate the destination slice. - if (!TGasPolicy.UpdateMemoryCost(ref gas, in destOffset, size, vm.VmState)) - return EvmExceptionType.OutOfGas; - - // Get the source slice; if the requested range exceeds the buffer length, it is zero-padded. - ZeroPaddedSpan slice = returnDataBuffer.Span.SliceWithZeroPadding(sourceOffset, (int)size); - if (!vm.VmState.Memory.TrySave(in destOffset, in slice)) goto OutOfGas; - - // Report the memory change if tracing is active. - if (TTracingInst.IsActive) - { - vm.TxTracer.ReportMemoryChange(destOffset, in slice); - } - } - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - StackUnderflow: - return EvmExceptionType.StackUnderflow; - AccessViolation: - return EvmExceptionType.AccessViolation; - } - - /// - /// Loads 32 bytes from the code's data section at the offset specified on the stack. - /// Pushes the zero-padded result onto the stack. - /// - /// The virtual machine instance. - /// Reference to the operand stack. - /// Reference to the remaining gas. - /// Reference to the program counter. - /// An representing success or an error. - [SkipLocalsInit] - public static EvmExceptionType InstructionDataLoad(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - // Ensure the instruction is only valid for non-legacy (EOF) code. - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - // Deduct gas required for data loading. - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.DataLoad)) - goto OutOfGas; - - // Pop the offset from the stack. - stack.PopUInt256(out UInt256 offset); - // Load 32 bytes from the data section at the given offset (with zero padding if necessary). - ZeroPaddedSpan bytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); - stack.PushBytes(bytes); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Loads 32 bytes from the data section using an immediate two-byte offset embedded in the code. - /// Advances the program counter accordingly. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.DataLoadN)) - goto OutOfGas; - - // Read a 16-bit immediate operand from the code. - ushort offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); - // Load the 32-byte word from the data section at the immediate offset. - ZeroPaddedSpan bytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); - stack.PushBytes(bytes); - - // Advance the program counter past the immediate operand. - programCounter += EofValidator.TWO_BYTE_LENGTH; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Pushes the size of the code's data section onto the stack. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.DataSize)) - goto OutOfGas; - - stack.PushUInt32((uint)codeInfo.DataSection.Length); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Copies a slice of bytes from the code's data section into the VM's memory. - /// The source offset, destination memory offset, and number of bytes are specified on the stack. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - // Pop destination memory offset, data section offset, and size. - if (!stack.PopUInt256(out UInt256 memOffset) || - !stack.PopUInt256(out UInt256 offset) || - !stack.PopUInt256(out UInt256 size)) - { - goto StackUnderflow; - } - - // Calculate memory expansion gas cost and deduct overall gas for data copy. - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.DataCopy + GasCostOf.Memory * EvmCalculations.Div32Ceiling(in size, out bool outOfGas)) - || outOfGas) - { - goto OutOfGas; - } - - if (!size.IsZero) - { - // Update memory cost for the destination region. - if (!TGasPolicy.UpdateMemoryCost(ref gas, in memOffset, size, vm.VmState)) - goto OutOfGas; - // Retrieve the slice from the data section with zero padding if necessary. - ZeroPaddedSpan dataSectionSlice = codeInfo.DataSection.SliceWithZeroPadding(offset, (int)size); - if (!vm.VmState.Memory.TrySave(in memOffset, in dataSectionSlice)) goto OutOfGas; - - if (TTracingInst.IsActive) - { - vm.TxTracer.ReportMemoryChange(memOffset, dataSectionSlice); - } - } - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Performs a relative jump in the code. - /// Reads a two-byte signed offset from the code section and adjusts the program counter accordingly. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionRelativeJump(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.RJump)) - goto OutOfGas; - - // Read a signed 16-bit offset and adjust the program counter. - short offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); - programCounter += EofValidator.TWO_BYTE_LENGTH + offset; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Conditionally performs a relative jump based on the top-of-stack condition. - /// Pops a condition value; if non-zero, jumps by the signed offset embedded in the code. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.RJumpi)) - goto OutOfGas; - - // Pop the condition word. - Span condition = stack.PopWord256(); - // Read the jump offset from the code. - short offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); - if (!condition.IsZero()) - { - // Apply the offset if the condition is non-zero. - programCounter += offset; - } - // Always advance past the immediate operand. - programCounter += EofValidator.TWO_BYTE_LENGTH; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Implements a jump table dispatch. - /// Uses the top-of-stack value as an index into a list of jump offsets, then jumps accordingly. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.RJumpv)) - goto OutOfGas; - - // Pop the table index from the stack. - stack.PopUInt256(out UInt256 indexValue); - ReadOnlySpan codeSection = codeInfo.CodeSection.Span; - - // Determine the number of cases in the jump table (first byte + one). - var count = codeSection[programCounter] + 1; - // Calculate the total immediate bytes to skip after processing the jump table. - var immediateCount = (ushort)(count * EofValidator.TWO_BYTE_LENGTH + EofValidator.ONE_BYTE_LENGTH); - if (indexValue < count) - { - // Calculate the jump offset from the corresponding entry in the jump table. - int case_v = programCounter + EofValidator.ONE_BYTE_LENGTH + (int)indexValue * EofValidator.TWO_BYTE_LENGTH; - int offset = codeSection.Slice(case_v, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); - programCounter += offset; - } - // Skip over the jump table immediateCount. - programCounter += immediateCount; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Performs a subroutine call within the code. - /// Sets up the return state and verifies stack and call depth constraints before transferring control. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - { - CodeInfo iCodeInfo = vm.VmState.Env.CodeInfo; - if (iCodeInfo.Version == 0) - goto BadInstruction; - - EofCodeInfo codeInfo = (EofCodeInfo)iCodeInfo; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.Callf)) - goto OutOfGas; - - ReadOnlySpan codeSection = codeInfo.CodeSection.Span; - // Read the immediate two-byte index for the target section. - var index = (int)codeSection.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); - // Get metadata for the target section. - (int inputCount, _, int maxStackHeight) = codeInfo.GetSectionMetadata(index); - - // Verify that invoking the subroutine will not exceed the maximum stack height. - if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) - { - goto StackOverflow; - } - - // Ensure there is room on the return stack. - if (vm.VmState.ReturnStackHead == Eof1.RETURN_STACK_MAX_HEIGHT) - goto InvalidSubroutineEntry; - - // Push current state onto the return stack. - vm.VmState.ReturnStack[vm.VmState.ReturnStackHead++] = new ReturnState - { - Index = vm.SectionIndex, - Height = stack.Head - inputCount, - Offset = programCounter + EofValidator.TWO_BYTE_LENGTH - }; - - // Set up the subroutine call. - vm.SectionIndex = index; - programCounter = codeInfo.CodeSectionOffset(index).Start; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - InvalidSubroutineEntry: - return EvmExceptionType.InvalidSubroutineEntry; - StackOverflow: - return EvmExceptionType.StackOverflow; - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Returns from a subroutine call by restoring the state from the return stack. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - { - CodeInfo codeInfo = vm.VmState.Env.CodeInfo; - if (codeInfo.Version == 0) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.Retf)) - goto OutOfGas; - - // Pop the return state from the return stack. - ReturnState stackFrame = vm.VmState.ReturnStack[--vm.VmState.ReturnStackHead]; - vm.SectionIndex = stackFrame.Index; - programCounter = stackFrame.Offset; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Performs an unconditional jump to a subroutine using a section index read from the code. - /// Verifies that the target section does not cause a stack overflow. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - { - CodeInfo iCodeInfo = vm.VmState.Env.CodeInfo; - if (iCodeInfo.Version == 0) - goto BadInstruction; - - EofCodeInfo codeInfo = (EofCodeInfo)iCodeInfo; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.Jumpf)) - goto OutOfGas; - - // Read the target section index from the code. - int index = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); - (int inputCount, _, int maxStackHeight) = codeInfo.GetSectionMetadata(index); - - // Check that the stack will not overflow after the jump. - if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) - { - goto StackOverflow; - } - vm.SectionIndex = index; - programCounter = codeInfo.CodeSectionOffset(index).Start; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackOverflow: - return EvmExceptionType.StackOverflow; - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Duplicates a stack item based on an immediate operand. - /// The immediate value (n) specifies that the (n+1)th element from the top is duplicated. - /// For EOF code only. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionEofDupN(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.Dupn)) - goto OutOfGas; - - // Read the immediate operand. - int imm = codeInfo.CodeSection.Span[programCounter]; - // Duplicate the (imm+1)th stack element. - EvmExceptionType result = stack.Dup(imm + 1); - - programCounter += 1; - - return result; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Swaps two stack items. The immediate operand specifies the swap distance. - /// Swaps the top-of-stack with the (n+1)th element. - /// For EOF code only. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionEofSwapN(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.Swapn)) - goto OutOfGas; - - // Immediate operand determines the swap index. - int n = 1 + (int)codeInfo.CodeSection.Span[programCounter]; - EvmExceptionType result = stack.Swap(n + 1); - - programCounter += 1; - - return result; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Exchanges two stack items using a combined immediate operand. - /// The high nibble and low nibble of the operand specify the two swap distances. - /// For EOF code only. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionEofExchange(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - if (vm.VmState.Env.CodeInfo is not EofCodeInfo codeInfo) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.Swapn)) - goto OutOfGas; - - ReadOnlySpan codeSection = codeInfo.CodeSection.Span; - // Extract two 4-bit values from the immediate operand. - int n = 1 + (int)(codeSection[programCounter] >> 0x04); - int m = 1 + (int)(codeSection[programCounter] & 0x0f); - - // Exchange the elements at the calculated positions. - stack.Exchange(n + 1, m + n + 1); - - programCounter += 1; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Implements the EOFCREATE instruction which creates a new contract using EOF semantics. - /// This method performs multiple steps including gas deductions, memory expansion, - /// reading immediate operands, balance checks, and preparing the execution environment for the new contract. - /// - /// - /// A tracing flag type to conditionally report events. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - Metrics.IncrementCreates(); - vm.ReturnData = null; - - IReleaseSpec spec = vm.Spec; - ExecutionEnvironment env = vm.VmState.Env; - if (env.CodeInfo.Version == 0) - goto BadInstruction; - - if (vm.VmState.IsStatic) - goto StaticCallViolation; - - // Cast the current code info to EOF-specific container type. - EofCodeInfo container = env.CodeInfo as EofCodeInfo; - ExecutionType currentContext = ExecutionType.EOFCREATE; - - // 1. Deduct the creation gas cost. - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.TxCreate)) - goto OutOfGas; - - ReadOnlySpan codeSection = container.CodeSection.Span; - // 2. Read the immediate operand for the init container index. - int initContainerIndex = codeSection[programCounter++]; - - // 3. Pop contract creation parameters from the stack. - if (!stack.PopUInt256(out UInt256 value) || - !stack.PopWord256(out Span salt) || - !stack.PopUInt256(out UInt256 dataOffset) || - !stack.PopUInt256(out UInt256 dataSize)) - { - goto OutOfGas; - } - - // 4. Charge for memory expansion for the input data. - if (!TGasPolicy.UpdateMemoryCost(ref gas, in dataOffset, dataSize, vm.VmState)) - goto OutOfGas; - - // 5. Load the init code (EOF subContainer) from the container using the given index. - ReadOnlyMemory initContainer = container.ContainerSection[(Range)container.ContainerSectionOffset(initContainerIndex)!.Value]; - // EIP-3860: Check that the init code size does not exceed the maximum allowed. - if (spec.IsEip3860Enabled) - { - if (initContainer.Length > spec.MaxInitCodeSize) - goto OutOfGas; - } - - // 6. Deduct gas for keccak256 hashing of the init code. - long numberOfWordsInInitCode = EvmCalculations.Div32Ceiling((UInt256)initContainer.Length, out bool outOfGas); - long hashCost = GasCostOf.Sha3Word * numberOfWordsInInitCode; - if (outOfGas || !TGasPolicy.UpdateGas(ref gas, hashCost)) - goto OutOfGas; - - IWorldState state = vm.WorldState; - // 7. Check call depth and caller's balance before proceeding with creation. - UInt256 balance = state.GetBalance(env.ExecutingAccount); - if (env.CallDepth >= MaxCallDepth || value > balance) - { - // In case of failure, do not consume additional gas. - vm.ReturnDataBuffer = Array.Empty(); - stack.PushZero(); - return EvmExceptionType.None; - } - - // 9. Determine gas available for the new contract execution, applying the 63/64 rule if enabled. - long gasAvailable = TGasPolicy.GetRemainingGas(in gas); - long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; - if (!TGasPolicy.UpdateGas(ref gas, callGas)) - goto OutOfGas; - - // 10. Increment the nonce of the sender account. - UInt256 accountNonce = state.GetNonce(env.ExecutingAccount); - UInt256 maxNonce = ulong.MaxValue; - if (accountNonce >= maxNonce) - { - vm.ReturnDataBuffer = Array.Empty(); - stack.PushZero(); - return EvmExceptionType.None; - } - state.IncrementNonce(env.ExecutingAccount); - - // 11. Calculate the new contract address. - Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initContainer.Span); - if (spec.UseHotAndColdStorage) - { - // Warm up the target address for subsequent storage accesses. - vm.VmState.AccessTracker.WarmUp(contractAddress); - } - - if (TTracingInst.IsActive) - vm.EndInstructionTrace(TGasPolicy.GetRemainingGas(in gas)); - - // Take a snapshot before modifying state for the new contract. - Snapshot snapshot = state.TakeSnapshot(); - - // EIP-7610: If the account already exists and is non-zero, then the creation fails. - if (state.IsNonZeroAccount(contractAddress, out bool accountExists)) - { - vm.ReturnDataBuffer = Array.Empty(); - stack.PushZero(); - return EvmExceptionType.None; - } - - // If the account is marked as dead, clear its storage. - if (state.IsDeadAccount(contractAddress)) - { - state.ClearStorage(contractAddress); - } - - // Deduct the transferred value from the caller's balance. - state.SubtractFromBalance(env.ExecutingAccount, value, spec); - - // Create new code info for the init code. - CodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(initContainer, spec, ValidationStrategy.ExtractHeader); - - // 8. Prepare the callData from the caller’s memory slice. - if (!vm.VmState.Memory.TryLoad(dataOffset, dataSize, out ReadOnlyMemory callData)) - goto OutOfGas; - - // Set up the execution environment for the new contract. - ExecutionEnvironment callEnv = ExecutionEnvironment.Rent( - codeInfo: codeInfo, - executingAccount: contractAddress, - caller: env.ExecutingAccount, - codeSource: null, - callDepth: env.CallDepth + 1, - transferValue: in value, - value: in value, - inputData: in callData); - - vm.ReturnData = VmState.RentFrame( - gas: TGasPolicy.CreateChildFrameGas(ref gas, callGas), - outputDestination: 0, - outputLength: 0, - executionType: currentContext, - isStatic: vm.VmState.IsStatic, - isCreateOnPreExistingAccount: accountExists, - env: callEnv, - stateForAccessLists: in vm.VmState.AccessTracker, - snapshot: in snapshot); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StaticCallViolation: - return EvmExceptionType.StaticCallViolation; - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Returns the contract creation result. - /// Extracts the deployment code from a specified container section and prepares the return data. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionReturnCode(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - { - // This instruction is only valid in create contexts. - if (!vm.VmState.ExecutionType.IsAnyCreateEof()) - goto BadInstruction; - - if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.ReturnCode)) - goto OutOfGas; - - IReleaseSpec spec = vm.Spec; - EofCodeInfo codeInfo = (EofCodeInfo)vm.VmState.Env.CodeInfo; - - // Read the container section index from the code. - byte sectionIdx = codeInfo.CodeSection.Span[programCounter++]; - // Retrieve the deployment code using the container section offset. - ReadOnlyMemory deployCode = codeInfo.ContainerSection[(Range)codeInfo.ContainerSectionOffset(sectionIdx)]; - EofCodeInfo deployCodeInfo = (EofCodeInfo)CodeInfoFactory.CreateCodeInfo(deployCode, spec, ValidationStrategy.ExtractHeader); - - // Pop memory offset and size for the return data. - stack.PopUInt256(out UInt256 a); - stack.PopUInt256(out UInt256 b); - - if (!TGasPolicy.UpdateMemoryCost(ref gas, in a, b, vm.VmState)) - goto OutOfGas; - - int projectedNewSize = (int)b + deployCodeInfo.DataSection.Length; - // Ensure the projected size is within valid bounds. - if (projectedNewSize < deployCodeInfo.EofContainer.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) - { - return EvmExceptionType.AccessViolation; - } - - // Load the memory slice as the return data buffer. - if (!vm.VmState.Memory.TryLoad(a, b, out vm.ReturnDataBuffer)) - goto OutOfGas; - - vm.ReturnData = deployCodeInfo; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Loads 32 bytes from the return data buffer using an offset from the stack, - /// then pushes the retrieved value onto the stack. - /// This instruction is only valid when EOF is enabled. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionReturnDataLoad(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TTracingInst : struct, IFlag - { - IReleaseSpec spec = vm.Spec; - CodeInfo codeInfo = vm.VmState.Env.CodeInfo; - if (!spec.IsEofEnabled || codeInfo.Version == 0) - goto BadInstruction; - - TGasPolicy.Consume(ref gas, GasCostOf.VeryLow); - - if (!stack.PopUInt256(out UInt256 offset)) - goto StackUnderflow; - - ZeroPaddedSpan slice = vm.ReturnDataBuffer.Span.SliceWithZeroPadding(offset, 32); - stack.PushBytes(slice); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; - BadInstruction: - return EvmExceptionType.BadInstruction; - } - - /// - /// Implements the EOF call instructions (CALL, DELEGATECALL, STATICCALL) for EOF-enabled contracts. - /// Pops the target address, callData parameters, and (if applicable) transfer value from the stack, - /// performs account access checks and gas adjustments, and then initiates the call. - /// - /// - /// The call type (standard, delegate, or static) that determines behavior and execution type. - /// - /// - /// A tracing flag type used to report VM state changes during the call. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionEofCall(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) - where TGasPolicy : struct, IGasPolicy - where TOpEofCall : struct, IOpEofCall - where TTracingInst : struct, IFlag - where TEip8037 : struct, IFlag - { - Metrics.IncrementCalls(); - - const int MIN_RETAINED_GAS = 5000; - - IReleaseSpec spec = vm.Spec; - vm.ReturnData = null; - ExecutionEnvironment env = vm.VmState.Env; - IWorldState state = vm.WorldState; - - // This instruction is only available for EOF-enabled contracts. - if (env.CodeInfo.Version == 0) - goto BadInstruction; - - // 1. Pop the target address (as 32 bytes) and memory offsets/length for the call data. - if (!stack.PopWord256(out Span targetBytes) || - !stack.PopUInt256(out UInt256 dataOffset) || - !stack.PopUInt256(out UInt256 dataLength)) - { - goto StackUnderflow; - } - - UInt256 transferValue; - UInt256 callValue; - // 2. Determine transfer values based on call type. - if (typeof(TOpEofCall) == typeof(OpEofStaticCall)) - { - transferValue = UInt256.Zero; - callValue = UInt256.Zero; - } - else if (typeof(TOpEofCall) == typeof(OpEofDelegateCall)) - { - transferValue = UInt256.Zero; - callValue = env.Value; - } - else if (stack.PopUInt256(out transferValue)) - { - callValue = transferValue; - } - else - { - goto StackUnderflow; - } - - // 3. For non-static calls, ensure that a non-zero transfer value is not used in a static context. - if (vm.VmState.IsStatic && !transferValue.IsZero) - goto StaticCallViolation; - // 4. Charge additional gas if a value is transferred in a standard call. - if (typeof(TOpEofCall) == typeof(OpEofCall) && !transferValue.IsZero && !TGasPolicy.UpdateGas(ref gas, GasCostOf.CallValue)) - goto OutOfGas; - - // 5. Validate that the targetBytes represent a proper 20-byte address (high 12 bytes must be zero). - if (!targetBytes[0..12].IsZero()) - goto AddressOutOfRange; - - Address caller = typeof(TOpEofCall) == typeof(OpEofDelegateCall) ? env.Caller : env.ExecutingAccount; - Address codeSource = new(targetBytes[12..].ToArray()); - // For delegate calls, the target remains the executing account. - Address target = typeof(TOpEofCall) == typeof(OpEofDelegateCall) - ? env.ExecutingAccount - : codeSource; - - // 6. Update memory cost for the call data. - if (!TGasPolicy.UpdateMemoryCost(ref gas, in dataOffset, in dataLength, vm.VmState)) - goto OutOfGas; - // 7. Account access gas: ensure target is warm or charge extra gas for cold access. - bool _ = vm.TxExecutionContext.CodeInfoRepository - .TryGetDelegation(codeSource, vm.Spec, out Address delegated); - if (!TGasPolicy.ConsumeAccountAccessGasWithDelegation(ref gas, vm.Spec, in vm.VmState.AccessTracker, - vm.TxTracer.IsTracingAccess, codeSource, delegated)) goto OutOfGas; - - // 8. If the target does not exist or is considered a "dead" account when value is transferred, - // charge for account creation. - if ((!spec.ClearEmptyAccountWhenTouched && !state.AccountExists(codeSource)) - || (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && state.IsDeadAccount(codeSource))) - { - if (TEip8037.IsActive switch - { - true => !TGasPolicy.ConsumeNewAccountCreation(ref gas), - false => !TGasPolicy.UpdateGas(ref gas, GasCostOf.NewAccount), - }) - goto OutOfGas; - } - - // 9. Compute the gas available to the callee after reserving a minimum. - long gasAvailable = TGasPolicy.GetRemainingGas(in gas); - long callGas = gasAvailable - Math.Max(gasAvailable / 64, MIN_RETAINED_GAS); - - // 10. Check that the call gas is sufficient, the caller has enough balance, and the call depth is within limits. - if (callGas < GasCostOf.CallStipend || - (!transferValue.IsZero && state.GetBalance(env.ExecutingAccount) < transferValue) || - env.CallDepth >= MaxCallDepth) - { - vm.ReturnData = null; - vm.ReturnDataBuffer = Array.Empty(); - stack.PushOne(); - - // If tracing is active, record additional details regarding the failure. - ITxTracer txTracer = vm.TxTracer; - if (TTracingInst.IsActive) - { - ReadOnlyMemory memoryTrace = vm.VmState.Memory.Inspect(in dataOffset, 32); - txTracer.ReportMemoryChange(dataOffset, memoryTrace.Span); - txTracer.ReportOperationRemainingGas(gasAvailable); - txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); - txTracer.ReportGasUpdateForVmTrace(callGas, gasAvailable); - } - - return EvmExceptionType.None; - } - - // 11. Retrieve and prepare the target code for execution. - CodeInfo targetCodeInfo = vm.CodeInfoRepository.GetCachedCodeInfo(codeSource, spec); - - // For delegate calls, calling a non-EOF (legacy) target is disallowed. - if (typeof(TOpEofCall) == typeof(OpEofDelegateCall) - && targetCodeInfo.Version == 0) - { - vm.ReturnData = null; - vm.ReturnDataBuffer = Array.Empty(); - stack.PushOne(); - return EvmExceptionType.None; - } - - // 12. Deduct gas for the call and prepare the call data. - if (!TGasPolicy.UpdateGas(ref gas, callGas) || - !vm.VmState.Memory.TryLoad(in dataOffset, dataLength, out ReadOnlyMemory callData)) - { - goto OutOfGas; - } - - // Snapshot the state before the call. - Snapshot snapshot = state.TakeSnapshot(); - // Deduct the transferred value from the caller. - state.SubtractFromBalance(caller, transferValue, spec); - - // Set up the new execution environment for the call. - ExecutionEnvironment callEnv = ExecutionEnvironment.Rent( - codeInfo: targetCodeInfo, - executingAccount: target, - caller: caller, - codeSource: codeSource, - callDepth: env.CallDepth + 1, - transferValue: in transferValue, - value: in callValue, - inputData: in callData); - - vm.ReturnData = VmState.RentFrame( - gas: TGasPolicy.CreateChildFrameGas(ref gas, callGas), - outputDestination: 0, - outputLength: 0, - executionType: TOpEofCall.ExecutionType, - isStatic: TOpEofCall.IsStatic || vm.VmState.IsStatic, - isCreateOnPreExistingAccount: false, - env: callEnv, - stateForAccessLists: in vm.VmState.AccessTracker, - snapshot: in snapshot); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; - OutOfGas: - return EvmExceptionType.OutOfGas; - BadInstruction: - return EvmExceptionType.BadInstruction; - StaticCallViolation: - return EvmExceptionType.StaticCallViolation; - AddressOutOfRange: - return EvmExceptionType.AddressOutOfRange; - } -} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index 586960334c1c..ff4e51eca118 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -572,7 +572,7 @@ public static EvmExceptionType InstructionSwap - /// EIP-8024: DUPN instruction for (non-EOF) code. + /// EIP-8024: DUPN instruction. /// Duplicates a stack item based on an immediate operand with extended encoding. /// [SkipLocalsInit] @@ -589,7 +589,7 @@ public static EvmExceptionType InstructionDupN(Virtual } /// - /// EIP-8024: SWAPN instruction for (non-EOF) code. + /// EIP-8024: SWAPN instruction. /// Swaps top of stack with the Nth element, where N is decoded from the immediate. /// [SkipLocalsInit] @@ -606,7 +606,7 @@ public static EvmExceptionType InstructionSwapN(Virtua } /// - /// EIP-8024: EXCHANGE instruction for (non-EOF) code. + /// EIP-8024: EXCHANGE instruction. /// Exchanges stack items at positions n and m from the top. /// [SkipLocalsInit] diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 493961309ec7..f4ca8fbebeb8 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -100,12 +100,9 @@ internal static unsafe partial class EvmInstructions lookup[(int)Instruction.RETURNDATACOPY] = &InstructionReturnDataCopy; } - // Extended code hash opcode handling. if (spec.ExtCodeHashOpcodeEnabled) { - lookup[(int)Instruction.EXTCODEHASH] = spec.IsEofEnabled ? - &InstructionExtCodeHashEof : - &InstructionExtCodeHash; + lookup[(int)Instruction.EXTCODEHASH] = &InstructionExtCodeHash; } lookup[(int)Instruction.BLOCKHASH] = &InstructionBlockHash; @@ -262,8 +259,7 @@ internal static unsafe partial class EvmInstructions lookup[(int)Instruction.LOG3] = &InstructionLog; lookup[(int)Instruction.LOG4] = &InstructionLog; - // EIP-8024: Backward-compatible stack operations for legacy code. - // These are registered first and will be overridden by EOF handlers if EOF is enabled. + // EIP-8024 stack operations. if (spec.IsEip8024Enabled) { lookup[(int)Instruction.DUPN] = &InstructionDupN; @@ -271,26 +267,6 @@ internal static unsafe partial class EvmInstructions lookup[(int)Instruction.EXCHANGE] = &InstructionExchange; } - // Extended opcodes for EO (EoF) mode. - if (spec.IsEofEnabled) - { - lookup[(int)Instruction.DATALOAD] = &InstructionDataLoad; - lookup[(int)Instruction.DATALOADN] = &InstructionDataLoadN; - lookup[(int)Instruction.DATASIZE] = &InstructionDataSize; - lookup[(int)Instruction.DATACOPY] = &InstructionDataCopy; - lookup[(int)Instruction.RJUMP] = &InstructionRelativeJump; - lookup[(int)Instruction.RJUMPI] = &InstructionRelativeJumpIf; - lookup[(int)Instruction.RJUMPV] = &InstructionJumpTable; - lookup[(int)Instruction.CALLF] = &InstructionCallFunction; - lookup[(int)Instruction.RETF] = &InstructionReturnFunction; - lookup[(int)Instruction.JUMPF] = &InstructionJumpFunction; - lookup[(int)Instruction.DUPN] = &InstructionEofDupN; - lookup[(int)Instruction.SWAPN] = &InstructionEofSwapN; - lookup[(int)Instruction.EXCHANGE] = &InstructionEofExchange; - lookup[(int)Instruction.EOFCREATE] = &InstructionEofCreate; - lookup[(int)Instruction.RETURNCODE] = &InstructionReturnCode; - } - // Contract creation and call opcodes. lookup[(int)Instruction.CREATE] = spec.IsEip8037Enabled ? &InstructionCreate @@ -327,7 +303,6 @@ internal static unsafe partial class EvmInstructions : &InstructionCreate; } - lookup[(int)Instruction.RETURNDATALOAD] = &InstructionReturnDataLoad; if (spec.StaticCallEnabled) { lookup[(int)Instruction.STATICCALL] = (spec.IsEip8037Enabled, spec.IsEip7708Enabled) switch @@ -339,26 +314,6 @@ internal static unsafe partial class EvmInstructions }; } - // Extended call opcodes in EO mode. - if (spec.IsEofEnabled) - { - lookup[(int)Instruction.EXTCALL] = spec.IsEip8037Enabled - ? &InstructionEofCall - : &InstructionEofCall; - if (spec.DelegateCallEnabled) - { - lookup[(int)Instruction.EXTDELEGATECALL] = spec.IsEip8037Enabled - ? &InstructionEofCall - : &InstructionEofCall; - } - if (spec.StaticCallEnabled) - { - lookup[(int)Instruction.EXTSTATICCALL] = spec.IsEip8037Enabled - ? &InstructionEofCall - : &InstructionEofCall; - } - } - if (spec.RevertOpcodeEnabled) { lookup[(int)Instruction.REVERT] = &InstructionRevert; diff --git a/src/Nethermind/Nethermind.Evm/StackPool.cs b/src/Nethermind/Nethermind.Evm/StackPool.cs index 3c5a13093888..43cb16ff329a 100644 --- a/src/Nethermind/Nethermind.Evm/StackPool.cs +++ b/src/Nethermind/Nethermind.Evm/StackPool.cs @@ -14,21 +14,14 @@ internal sealed class StackPool { // Also have parallel prewarming and Rpc calls private const int MaxStacksPooled = MaxCallDepth * 2; - private readonly struct StackItem(byte[] dataStack, ReturnState[] returnStack) + private readonly struct StackItem(byte[] dataStack) { public readonly byte[] DataStack = dataStack; - public readonly ReturnState[] ReturnStack = returnStack; } private readonly ConcurrentQueue _stackPool = new(); - /// - /// The word 'return' acts here once as a verb 'to return stack to the pool' and once as a part of the - /// compound noun 'return stack' which is a stack of subroutine return values. - /// - /// - /// - public void ReturnStacks(byte[] dataStack, ReturnState[] returnStack) + public void ReturnStacks(byte[] dataStack) { // Reserve a slot first - O(1) bound without touching ConcurrentQueue.Count. if (Interlocked.Increment(ref _poolCount) > MaxStacksPooled) @@ -38,7 +31,7 @@ public void ReturnStacks(byte[] dataStack, ReturnState[] returnStack) return; } - _stackPool.Enqueue(new StackItem(dataStack, returnStack)); + _stackPool.Enqueue(new StackItem(dataStack)); } // Manual reservation count - upper bound on items actually in the queue. @@ -46,22 +39,17 @@ public void ReturnStacks(byte[] dataStack, ReturnState[] returnStack) public const int StackLength = (EvmStack.MaxStackSize + EvmStack.RegisterLength) * 32; - public (byte[], ReturnState[]) RentStacks() + public byte[] RentStacks() { if (Volatile.Read(ref _poolCount) > 0 && _stackPool.TryDequeue(out StackItem result)) { Interlocked.Decrement(ref _poolCount); - return (result.DataStack, result.ReturnStack); + return result.DataStack; } // Count was positive but we lost the race or the enqueuer has not published yet. // Include extra Vector256.Count and pin so we can align to 32 bytes. // This ensures the stack is properly aligned for SIMD operations. - return - ( - GC.AllocateUninitializedArray(StackLength + Vector256.Count, pinned: true), - new ReturnState[EvmStack.ReturnStackSize] - ); + return GC.AllocateUninitializedArray(StackLength + Vector256.Count, pinned: true); } } - diff --git a/src/Nethermind/Nethermind.Evm/StatusCode.cs b/src/Nethermind/Nethermind.Evm/StatusCode.cs index fd17b3b7e96c..67d23340f698 100644 --- a/src/Nethermind/Nethermind.Evm/StatusCode.cs +++ b/src/Nethermind/Nethermind.Evm/StatusCode.cs @@ -13,13 +13,4 @@ public static class StatusCode public const byte Success = 1; public static readonly ReadOnlyMemory SuccessBytes = Bytes.OneByte; } - public static class EofStatusCode - { - public const byte Success = 0; - public static readonly ReadOnlyMemory SuccessBytes = Bytes.ZeroByte; - public const byte Revert = 1; - public static readonly ReadOnlyMemory RevertBytes = Bytes.OneByte; - public const byte Failure = 2; - public static readonly ReadOnlyMemory FailureBytes = Bytes.TwoByte; - } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs index 48cabf1cc40c..c95c6b06c967 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs @@ -190,12 +190,12 @@ public void MarkAsFailed(Address recipient, in GasConsumed gasSpent, byte[] outp } } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { token.ThrowIfCancellationRequested(); if (innerTracer.IsTracingInstructions) { - innerTracer.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); + innerTracer.StartOperation(pc, opcode, gas, env); } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs index 5da765b458e2..c59c0cc9b25b 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -152,14 +152,14 @@ public void MarkAsFailed(Address recipient, in GasConsumed gasSpent, byte[] outp } } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { for (int index = 0; index < _txTracers.Count; index++) { ITxTracer innerTracer = _txTracers[index]; if (innerTracer.IsTracingInstructions) { - innerTracer.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); + innerTracer.StartOperation(pc, opcode, gas, env); } } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs index 9e4698764896..1c3ba7424570 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs @@ -196,8 +196,8 @@ public void MarkAsSuccess(Address recipient, in GasConsumed gasSpent, byte[] out public void MarkAsFailed(Address recipient, in GasConsumed gasSpent, byte[] output, string error, Hash256? stateRoot = null) => InnerTracer.MarkAsFailed(recipient, gasSpent, output, error, stateRoot); - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) - => InnerTracer.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + => InnerTracer.StartOperation(pc, opcode, gas, env); public void ReportOperationError(EvmExceptionType error) => InnerTracer.ReportOperationError(error); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs index b699a4380f06..0c619bbef5b2 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs @@ -179,10 +179,8 @@ public interface ITxTracer : IWorldStateTracer, IDisposable /// /// /// - /// - /// /// Depends on - void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0); + void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env); /// /// diff --git a/src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs index 588f69b5179f..54b46bb79663 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs @@ -26,7 +26,7 @@ public override void MarkAsSuccess(Address recipient, in GasConsumed gasSpent, b => ThrowInvalidOperationException(); public override void MarkAsFailed(Address recipient, in GasConsumed gasSpent, byte[] output, string? error, Hash256? stateRoot = null) => ThrowInvalidOperationException(); - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) => ThrowInvalidOperationException(); public override void ReportOperationError(EvmExceptionType error) diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index 9fa345e8ae1a..d7b2f5cabb13 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs @@ -54,7 +54,7 @@ public virtual void ReportStorageChange(in StorageCell storageCell, byte[] befor public virtual void ReportStorageRead(in StorageCell storageCell) { } public virtual void MarkAsSuccess(Address recipient, in GasConsumed gasSpent, byte[] output, LogEntry[] logs, Hash256? stateRoot = null) { } public virtual void MarkAsFailed(Address recipient, in GasConsumed gasSpent, byte[] output, string? error, Hash256? stateRoot = null) { } - public virtual void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { } + public virtual void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { } public virtual void ReportOperationError(EvmExceptionType error) { } public virtual void ReportOperationRemainingGas(long gas) { } public virtual void ReportLog(LogEntry log) { } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index c92ba6003eca..85eaf2cfa5bb 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -12,14 +12,12 @@ using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Evm.CodeAnalysis; -using Nethermind.Evm.EvmObjectFormat.Handlers; using Nethermind.Evm.GasPolicy; using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Evm.State; using Nethermind.Evm.Tracing.State; -using static Nethermind.Evm.EvmObjectFormat.EofValidator; namespace Nethermind.Evm.TransactionProcessing { @@ -289,13 +287,13 @@ protected virtual TransactionResult Execute(Transaction tx, ITxTracer tracer, Ex if (statusCode == StatusCode.Failure) { - byte[] output = substate.ShouldRevert ? substate.Output.Bytes.ToArray() : []; + byte[] output = substate.ShouldRevert ? substate.Output.ToArray() : []; tracer.MarkAsFailed(env.ExecutingAccount, spentGas, output, substate.Error, stateRoot); } else { LogEntry[] logs = substate.Logs.Count != 0 ? substate.Logs.ToArray() : []; - tracer.MarkAsSuccess(env.ExecutingAccount, spentGas, substate.Output.Bytes.ToArray(), logs, stateRoot); + tracer.MarkAsSuccess(env.ExecutingAccount, spentGas, substate.Output.ToArray(), logs, stateRoot); } } @@ -655,10 +653,7 @@ private TransactionResult BuildExecutionEnvironment( ReadOnlyMemory inputData = tx.IsMessageCall ? tx.Data : default; if (tx.IsContractCreation) { - if (CodeInfoFactory.CreateInitCodeInfo(tx.Data, spec, out codeInfo, out ReadOnlyMemory trailingData)) - { - inputData = trailingData; - } + codeInfo = CodeInfoFactory.CreateCodeInfo(tx.Data); } else { @@ -724,7 +719,7 @@ private int ExecuteEvmCall( if (tx.IsContractCreation) { // if transaction is a contract creation then recipient address is the contract deployment address - if (!PrepareAccountForContractDeployment(env.ExecutingAccount, _codeInfoRepository, spec)) + if (!PrepareDeployment(env.ExecutingAccount)) { goto FailContractCreate; } @@ -732,7 +727,6 @@ private int ExecuteEvmCall( } else { - // If EOF header parsing or full container validation fails, transaction is considered valid and failing. // Gas for initcode execution is not consumed, only intrinsic creation transaction costs are charged. long minimalGasLong = TGasPolicy.GetRemainingGas(gas.MinimalGas); gasConsumed = minimalGasLong; @@ -742,7 +736,7 @@ private int ExecuteEvmCall( goto Complete; } - ExecutionType executionType = tx.IsContractCreation ? ((spec.IsEofEnabled && tx.IsEofContractCreation) ? ExecutionType.TXCREATE : ExecutionType.CREATE) : ExecutionType.TRANSACTION; + ExecutionType executionType = tx.IsContractCreation ? ExecutionType.CREATE : ExecutionType.TRANSACTION; using (VmState state = VmState.RentTopLevel(gasAvailable, executionType, env, in accessedItems, in snapshot)) { @@ -767,19 +761,9 @@ private int ExecuteEvmCall( { if (tx.IsContractCreation) { - if (!spec.IsEofEnabled || tx.IsLegacyContractCreation) - { - if (!DeployLegacyContract(spec, env.ExecutingAccount, in substate, in accessedItems, ref gasAvailable)) - { - goto FailContractCreate; - } - } - else + if (!DeployContract(spec, env.ExecutingAccount, in substate, in accessedItems, ref gasAvailable)) { - if (!DeployEofContract(spec, env.ExecutingAccount, in substate, in accessedItems, ref gasAvailable)) - { - goto FailContractCreate; - } + goto FailContractCreate; } } @@ -856,53 +840,16 @@ protected virtual GasConsumed RefundOnFailContractCreation(Transaction tx, Block return new GasConsumed(spentGas, spentGas, blockGas, blockStateGas); } - protected virtual bool DeployLegacyContract(IReleaseSpec spec, Address codeOwner, in TransactionSubstate substate, in StackAccessTracker accessedItems, ref TGasPolicy unspentGas) + protected virtual bool DeployContract(IReleaseSpec spec, Address codeOwner, in TransactionSubstate substate, in StackAccessTracker accessedItems, ref TGasPolicy unspentGas) { - if (!CodeDepositHandler.CalculateCost(spec, substate.Output.Bytes.Length, out long regularDepositCost, out long stateDepositCost)) + if (!CodeDepositHandler.CalculateCost(spec, substate.Output.Length, out long regularDepositCost, out long stateDepositCost)) return false; - if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output.Bytes, 0)) + if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output)) return false; // Copy the bytes so it's not live memory that will be used in another tx. - return TryChargeCodeDeposit(spec, codeOwner, in accessedItems, ref unspentGas, regularDepositCost, stateDepositCost, substate.Output.Bytes.ToArray()); - } - - private bool DeployEofContract(IReleaseSpec spec, Address codeOwner, in TransactionSubstate substate, in StackAccessTracker accessedItems, ref TGasPolicy unspentGas) - { - // 1 - load deploy EOF subContainer at deploy_container_index in the container from which RETURNCODE is executed - ReadOnlySpan auxExtraData = substate.Output.Bytes.Span; - EofCodeInfo deployCodeInfo = (EofCodeInfo)substate.Output.DeployCode; - - if (!CodeDepositHandler.CalculateCost(spec, deployCodeInfo.Code.Length + auxExtraData.Length, out long regularDepositCost, out long stateDepositCost)) - return false; - - int codeLength = deployCodeInfo.Code.Length + auxExtraData.Length; - // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - if (codeLength > spec.MaxCodeSize) - return false; - - // 2 - concatenate data section with (aux_data_offset, aux_data_offset + aux_data_size) memory segment and update data size in the header - byte[] bytecodeResult = new byte[codeLength]; - // 2 - 1 - 1 - copy old container - deployCodeInfo.Code.Span.CopyTo(bytecodeResult); - // 2 - 1 - 2 - copy aux data to dataSection - auxExtraData.CopyTo(bytecodeResult.AsSpan(deployCodeInfo.Code.Length)); - - // 2 - 2 - update data section size in the header u16 - int dataSubHeaderSectionStart = - VERSION_OFFSET // magic + version - + Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) - + ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) - + (ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections?.Count) ?? 0 // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) ?? (0 bytes if no container section is available) - + ONE_BYTE_LENGTH; // data section separator - - ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); - bytecodeResult[dataSubHeaderSectionStart + 1] = (byte)(dataSize >> 8); - bytecodeResult[dataSubHeaderSectionStart + 2] = (byte)(dataSize & 0xFF); - - // 4 - set state[new_address].code to the updated deploy container - return TryChargeCodeDeposit(spec, codeOwner, in accessedItems, ref unspentGas, regularDepositCost, stateDepositCost, bytecodeResult); + return TryChargeCodeDeposit(spec, codeOwner, in accessedItems, ref unspentGas, regularDepositCost, stateDepositCost, substate.Output.ToArray()); } private bool TryChargeCodeDeposit( @@ -971,7 +918,7 @@ protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec } } - protected bool PrepareAccountForContractDeployment(Address contractAddress, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec) + protected bool PrepareDeployment(Address contractAddress) { if (WorldState.IsNonZeroAccount(contractAddress, out _)) { diff --git a/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs b/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs index 9b41c6f195ea..83ccccc27ea9 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs @@ -52,7 +52,7 @@ public readonly ref struct TransactionSubstate public string? Error { get; } public string? SubstateError { get; } public EvmExceptionType EvmExceptionType { get; } - public (CodeInfo DeployCode, ReadOnlyMemory Bytes) Output { get; } + public ReadOnlyMemory Output { get; } public bool ShouldRevert { get; } public long Refund { get; } public JournalCollection Logs => _logs ?? _emptyLogs; @@ -69,18 +69,7 @@ public TransactionSubstate(EvmExceptionType exceptionType, bool isTracerConnecte ShouldRevert = false; } - public static TransactionSubstate FailedInitCode => new("Eip 7698: Invalid CreateTx InitCode"); - - private TransactionSubstate(string errorCode) - { - Error = errorCode; - Refund = 0; - _destroyList = _emptyDestroyList; - _logs = _emptyLogs; - ShouldRevert = true; - } - - public TransactionSubstate((CodeInfo eofDeployCode, ReadOnlyMemory bytes) output, + public TransactionSubstate(ReadOnlyMemory bytes, long refund, IHashSetEnumerableCollection
destroyList, JournalCollection logs, @@ -90,7 +79,7 @@ public TransactionSubstate((CodeInfo eofDeployCode, ReadOnlyMemory bytes) ILogger logger = default) { _logger = logger; - Output = output; + Output = bytes; Refund = refund; _destroyList = destroyList; _logs = logs; @@ -108,10 +97,10 @@ public TransactionSubstate((CodeInfo eofDeployCode, ReadOnlyMemory bytes) if (!isTracerConnected) return; - if (Output.Bytes.IsEmpty) + if (Output.IsEmpty) return; - ReadOnlySpan span = Output.Bytes.Span; + ReadOnlySpan span = Output.Span; Error = TryGetErrorMessage(span) ?? EncodeErrorMessage(span); } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index fbe43230bbfc..cca5ad321e84 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -14,14 +14,12 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Evm.CodeAnalysis; -using Nethermind.Evm.EvmObjectFormat.Handlers; using Nethermind.Evm.GasPolicy; using Nethermind.Evm.Precompiles; using Nethermind.Evm.Tracing; using Nethermind.Logging; using Nethermind.Evm.State; -using static Nethermind.Evm.EvmObjectFormat.EofValidator; using static Nethermind.Evm.VirtualMachineStatics; #if DEBUG @@ -47,9 +45,8 @@ public sealed class EthereumVirtualMachine( ///
public static class VirtualMachineStatics { - public const int MaxCallDepth = Eof1.RETURN_STACK_MAX_HEIGHT; + public const int MaxCallDepth = 1024; public static readonly UInt256 P255Int = new(0, 0, 0, 9223372036854775808); // 2^255 - public static readonly byte[] EofHash256 = KeccakHash.ComputeHashBytes(EvmObjectFormat.EofValidator.MAGIC); public static ref readonly UInt256 P255 => ref P255Int; public static readonly UInt256 BigInt256 = 256; public static readonly UInt256 BigInt32 = 32; @@ -124,7 +121,6 @@ public unsafe partial class VirtualMachine( public void SetTxExecutionContext(in TxExecutionContext txExecutionContext) => _txExecutionContext = txExecutionContext; public VmState VmState { get => _currentState; protected set => _currentState = value; } - public int SectionIndex { get; set; } public int OpCodeCount { get; set; } /// @@ -220,7 +216,7 @@ public TransactionSubstate ExecuteTransaction( } else { - callResult = CallResult.InvalidCodeException; + callResult = new(EvmExceptionType.InvalidCode); } // If the call did not finish with a return, set up the next call frame and continue. @@ -274,22 +270,11 @@ public TransactionSubstate ExecuteTransaction( if (previousState.ExecutionType.IsAnyCreate()) { PrepareCreateData(previousState, ref previousCallOutput); - if (previousState.ExecutionType.IsAnyCreateLegacy()) - { - HandleLegacyCreate( - in callResult, - previousState, - gasAvailableForCodeDeposit, - ref previousStateSucceeded); - } - else if (previousState.ExecutionType.IsAnyCreateEof()) - { - HandleEofCreate( - in callResult, - previousState, - gasAvailableForCodeDeposit, - ref previousStateSucceeded); - } + HandleCreate( + in callResult, + previousState, + gasAvailableForCodeDeposit, + ref previousStateSucceeded); } else { @@ -350,12 +335,11 @@ protected ZeroPaddedSpan HandleRegularReturn(scoped in CallResult where TTracingInst : struct, IFlag { ZeroPaddedSpan previousCallOutput; - ReturnDataBuffer = callResult.Output.Bytes; - _previousCallResult = previousState.ExecutionType.IsAnyCallEof() ? EofStatusCode.SuccessBytes : - callResult.PrecompileSuccess.HasValue + ReturnDataBuffer = callResult.Output; + _previousCallResult = callResult.PrecompileSuccess.HasValue ? (callResult.PrecompileSuccess.Value ? StatusCode.SuccessBytes : StatusCode.FailureBytes) : StatusCode.SuccessBytes; - previousCallOutput = callResult.Output.Bytes.Span.SliceWithZeroPadding(0, Math.Min(callResult.Output.Bytes.Length, (int)previousState.OutputLength)); + previousCallOutput = callResult.Output.Span.SliceWithZeroPadding(0, Math.Min(callResult.Output.Length, (int)previousState.OutputLength)); _previousCallOutputDestination = (ulong)previousState.OutputDestination; if (previousState.IsPrecompile) { @@ -373,63 +357,9 @@ protected ZeroPaddedSpan HandleRegularReturn(scoped in CallResult return previousCallOutput; } - - protected void HandleEofCreate(in CallResult callResult, VmState previousState, long gasAvailableForCodeDeposit, ref bool previousStateSucceeded) - { - Address callCodeOwner = previousState.Env.ExecutingAccount; - // ReturnCode was called with a container index and auxdata - // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCODE is executed - ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; - EofCodeInfo deployCodeInfo = (EofCodeInfo)callResult.Output.Container; - - // 2 - concatenate data section with (aux_data_offset, aux_data_offset + aux_data_size) memory segment and update data size in the header - Span bytecodeResult = new byte[deployCodeInfo.Code.Length + auxExtraData.Length]; - // 2 - 1 - 1 - copy old container - deployCodeInfo.Code.Span.CopyTo(bytecodeResult); - // 2 - 1 - 2 - copy aux data to dataSection - auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.Code.Length..]); - - // 2 - 2 - update data section size in the header u16 - int dataSubHeaderSectionStart = - // magic + version - VERSION_OFFSET - // type section : (1 byte of separator + 2 bytes for size) - + Eof1.MINIMUM_HEADER_SECTION_SIZE - // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) - + ONE_BYTE_LENGTH - + TWO_BYTE_LENGTH - + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.CodeSections.Count - // container section - + (deployCodeInfo.EofContainer.Header.ContainerSections is null - // bytes if no container section is available - ? 0 - // 1 byte of separator + (ContainerSections count) * 2 bytes for size - : ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections.Value.Count) - // data section separator - + ONE_BYTE_LENGTH; - - ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); - bytecodeResult[dataSubHeaderSectionStart + 1] = (byte)(dataSize >> 8); - bytecodeResult[dataSubHeaderSectionStart + 2] = (byte)(dataSize & 0xFF); - - byte[] bytecodeResultArray = bytecodeResult.ToArray(); - - IReleaseSpec spec = BlockExecutionContext.Spec; - // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - if (!CodeDepositHandler.CalculateCost(spec, bytecodeResultArray.Length, out long regularDepositCost, out long stateDepositCost)) - { - regularDepositCost = long.MaxValue; - stateDepositCost = long.MaxValue; - } - - bool invalidCode = bytecodeResultArray.Length > spec.MaxCodeSize; - TryChargeAndDepositCode(previousState, gasAvailableForCodeDeposit, ref previousStateSucceeded, - regularDepositCost, stateDepositCost, invalidCode, bytecodeResultArray); - } - /// - /// Handles the code deposit for a legacy contract creation operation. - /// This method calculates the gas cost for depositing the contract code using legacy rules, + /// Handles the code deposit for a contract creation operation. + /// This method calculates code deposit gas, /// validates the code, and either deposits the code or reverts the world state if the deposit fails. /// /// @@ -445,22 +375,22 @@ protected void HandleEofCreate(in CallResult callResult, VmState pre /// /// A reference flag indicating whether the previous call frame executed successfully. This flag is set to false if the deposit fails. /// - protected void HandleLegacyCreate( + protected void HandleCreate( in CallResult callResult, VmState previousState, long gasAvailableForCodeDeposit, ref bool previousStateSucceeded) { IReleaseSpec spec = BlockExecutionContext.Spec; - if (!CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length, out long regularDepositCost, out long stateDepositCost)) + if (!CodeDepositHandler.CalculateCost(spec, callResult.Output.Length, out long regularDepositCost, out long stateDepositCost)) { regularDepositCost = long.MaxValue; stateDepositCost = long.MaxValue; } - bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(spec, callResult.Output.Bytes); + bool invalidCode = CodeDepositHandler.CodeIsInvalid(spec, callResult.Output); TryChargeAndDepositCode(previousState, gasAvailableForCodeDeposit, ref previousStateSucceeded, - regularDepositCost, stateDepositCost, invalidCode, callResult.Output.Bytes); + regularDepositCost, stateDepositCost, invalidCode, callResult.Output); } protected TransactionSubstate PrepareTopLevelSubstate(scoped in CallResult callResult) @@ -559,19 +489,12 @@ protected void HandleRevert(VmState previousState, in CallResult cal _worldState.Restore(previousState.Snapshot); // Cache the output bytes from the call result to avoid multiple property accesses. - ReadOnlyMemory outputBytes = callResult.Output.Bytes; + ReadOnlyMemory outputBytes = callResult.Output; // Set the return data buffer to the output bytes from the failed call. ReturnDataBuffer = outputBytes; - // Determine the appropriate failure status code. - // For calls with EOF semantics, differentiate between precompile failure and regular revert. - // Otherwise, use the standard failure status code. - _previousCallResult = previousState.ExecutionType.IsAnyCallEof() - ? (callResult.PrecompileSuccess is not null - ? EofStatusCode.FailureBytes - : EofStatusCode.RevertBytes) - : StatusCode.FailureBytes; + _previousCallResult = StatusCode.FailureBytes; // Slice the output bytes, zero-padding if necessary, to match the expected output length. // This ensures that the returned data conforms to the caller's output length expectations. @@ -647,11 +570,7 @@ protected TransactionSubstate HandleFailure(Exception failure, str return new TransactionSubstate(finalErrorType, txTracer.IsTracing, substateError); } - // For nested call frames, prepare to revert to the parent frame. - // Set the previous call result to a failure code depending on the call type. - _previousCallResult = _currentState.ExecutionType.IsAnyCallEof() - ? EofStatusCode.FailureBytes - : StatusCode.FailureBytes; + _previousCallResult = StatusCode.FailureBytes; // Reset output destination and return data. _previousCallOutputDestination = UInt256.Zero; @@ -745,10 +664,7 @@ protected TransactionSubstate HandleException(scoped in CallResult callResult, s return new TransactionSubstate(callResult.ExceptionType, txTracer.IsTracing); } - // For nested calls, mark the previous call result as a failure code based on the call's EOF semantics. - _previousCallResult = _currentState.ExecutionType.IsAnyCallEof() - ? EofStatusCode.FailureBytes - : StatusCode.FailureBytes; + _previousCallResult = StatusCode.FailureBytes; // Reset output destination and clear return data. _previousCallOutputDestination = UInt256.Zero; @@ -879,10 +795,10 @@ protected void TraceTransactionActionEnd(VmState currentState, in Ca { IReleaseSpec spec = BlockExecutionContext.Spec; // Calculate the gas cost required for depositing the contract code based on the length of the output. - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Length); // Cache the output bytes for reuse in the tracing reports. - ReadOnlyMemory outputBytes = callResult.Output.Bytes; + ReadOnlyMemory outputBytes = callResult.Output; // If an exception occurred during execution, report the error immediately. if (callResult.IsException) @@ -916,7 +832,7 @@ protected void TraceTransactionActionEnd(VmState currentState, in Ca } } // If the generated code is invalid (e.g., violates EIP-3541 by starting with 0xEF), report an invalid code error. - else if (CodeDepositHandler.CodeIsInvalid(spec, outputBytes, callResult.FromVersion)) + else if (CodeDepositHandler.CodeIsInvalid(spec, outputBytes)) { _txTracer.ReportActionError(EvmExceptionType.InvalidCode); } @@ -979,7 +895,7 @@ private CallResult RunPrecompile(VmState state) if ((ulong)baseGasCost + (ulong)dataGasCost > (ulong)long.MaxValue || !TGasPolicy.UpdateGas(ref gas, baseGasCost + dataGasCost)) { - return new(output: default, precompileSuccess: false, fromVersion: 0, shouldRevert: true, EvmExceptionType.OutOfGas); + return new(default, precompileSuccess: false, shouldRevert: true, EvmExceptionType.OutOfGas); } state.Gas = gas; @@ -991,7 +907,6 @@ private CallResult RunPrecompile(VmState state) return new( success ? output.Data : [], precompileSuccess: success, - fromVersion: 0, shouldRevert: !success, exceptionType: !success ? EvmExceptionType.PrecompileFailure : EvmExceptionType.None ) @@ -1008,7 +923,7 @@ private CallResult RunPrecompile(VmState state) catch (Exception exception) { if (_logger.IsError) LogExecutionException(precompile, exception); - return new(output: default, precompileSuccess: false, fromVersion: 0, shouldRevert: true); + return new(default, precompileSuccess: false, shouldRevert: true); } [MethodImpl(MethodImplOptions.NoInlining)] @@ -1140,11 +1055,11 @@ protected CallResult ExecuteCall( Empty: // Return an empty CallResult if there is no machine code to execute. - return CallResult.Empty(vmState.Env.CodeInfo.Version); + return CallResult.Empty(); OutOfGas: // Return an out-of-gas CallResult if updating the memory cost fails. - return CallResult.OutOfGasException; + return new(EvmExceptionType.OutOfGas); } /// @@ -1183,9 +1098,8 @@ protected CallResult RunByteCode( where TTracingInst : struct, IFlag where TCancelable : struct, IFlag { - // Reset return data and set the current section index from the VM state. + // Reset return data before executing the current frame. ReturnData = null; - SectionIndex = VmState.FunctionIndex; // Retrieve the code information and create a read-only span of instructions. CodeInfo codeInfo = VmState.Env.CodeInfo; @@ -1297,7 +1211,7 @@ protected CallResult RunByteCode( #if DEBUG debugger?.TryWait(ref _currentState, ref programCounter, ref gas, ref stack.Head); #endif - return CallResult.Empty(codeInfo.Version); + return CallResult.Empty(); DataReturn: #if DEBUG @@ -1308,17 +1222,17 @@ protected CallResult RunByteCode( if (ReturnData is byte[] data) { // Fall back to returning a CallResult with a byte array as the return data. - return new CallResult(null, data, null, codeInfo.Version); + return new CallResult(data, null); } else if (ReturnData is VmState state) { return new CallResult(state); } - return ReturnEof(codeInfo); + return new CallResult(ReturnDataBuffer, null); Revert: // Return a CallResult indicating a revert. - return new CallResult(null, (byte[])ReturnData, null, codeInfo.Version, shouldRevert: true, exceptionType); + return new CallResult((byte[])ReturnData, null, shouldRevert: true, exceptionType); OutOfGas: TGasPolicy.SetOutOfGas(ref gas); @@ -1341,10 +1255,6 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(codeBytes)), codeBytes.Length); } - [MethodImpl(MethodImplOptions.NoInlining)] - CallResult ReturnEof(CodeInfo codeInfo) - => new(ReturnData as EofCodeInfo, ReturnDataBuffer, null, codeInfo.Version); - [DoesNotReturn] static void ThrowOperationCanceledException() => throw new OperationCanceledException("Cancellation Requested"); } @@ -1355,16 +1265,13 @@ private CallResult GetFailureReturn(long gasAvailable, EvmExceptionType exceptio return exceptionType switch { - EvmExceptionType.OutOfGas => CallResult.OutOfGasException, - EvmExceptionType.BadInstruction => CallResult.InvalidInstructionException, - EvmExceptionType.StaticCallViolation => CallResult.StaticCallViolationException, - EvmExceptionType.InvalidSubroutineEntry => CallResult.InvalidSubroutineEntry, - EvmExceptionType.InvalidSubroutineReturn => CallResult.InvalidSubroutineReturn, - EvmExceptionType.StackOverflow => CallResult.StackOverflowException, - EvmExceptionType.StackUnderflow => CallResult.StackUnderflowException, - EvmExceptionType.InvalidJumpDestination => CallResult.InvalidJumpDestination, - EvmExceptionType.AccessViolation => CallResult.AccessViolationException, - EvmExceptionType.AddressOutOfRange => CallResult.InvalidAddressRange, + EvmExceptionType.OutOfGas or + EvmExceptionType.BadInstruction or + EvmExceptionType.StaticCallViolation or + EvmExceptionType.StackOverflow or + EvmExceptionType.StackUnderflow or + EvmExceptionType.InvalidJumpDestination or + EvmExceptionType.AccessViolation => new(exceptionType), _ => throw new ArgumentOutOfRangeException(nameof(exceptionType), exceptionType, "") }; } @@ -1376,18 +1283,13 @@ private void UpdateCurrentState(int pc, in TGasPolicy gas, int stackHead) state.ProgramCounter = pc; state.Gas = gas; state.DataStackHead = stackHead; - state.FunctionIndex = SectionIndex; } [MethodImpl(MethodImplOptions.NoInlining)] private void StartInstructionTrace(Instruction instruction, long gasAvailable, int programCounter, in EvmStack stackValue) { VmState vmState = VmState; - int sectionIndex = SectionIndex; - - bool isEofFrame = vmState.Env.CodeInfo.Version > 0; - _txTracer.StartOperation(programCounter, instruction, gasAvailable, vmState.Env, - isEofFrame ? sectionIndex : 0, isEofFrame ? vmState.ReturnStackHead + 1 : 0); + _txTracer.StartOperation(programCounter, instruction, gasAvailable, vmState.Env); if (_txTracer.IsTracingMemory) { _txTracer.SetOperationMemory(vmState.Memory.GetTrace()); diff --git a/src/Nethermind/Nethermind.Evm/VmState.cs b/src/Nethermind/Nethermind.Evm/VmState.cs index ed6e9b91f55a..b6bfc522530d 100644 --- a/src/Nethermind/Nethermind.Evm/VmState.cs +++ b/src/Nethermind/Nethermind.Evm/VmState.cs @@ -22,74 +22,15 @@ public class VmState : IDisposable private static readonly ConcurrentQueue> _statePool = new(); private static readonly StackPool _stackPool = new(); - /* - Type layout for 'EvmState' - Size: 176 bytes. Paddings: 5 bytes (%3 of empty space) - |=======================================================================| - | Object Header (8 bytes) | - |-----------------------------------------------------------------------| - | Method Table Ptr (8 bytes) | - |=======================================================================| - | 0-7: Byte[] DataStack (8 bytes) | - |-----------------------------------------------------------------------| - | 8-15: ReturnState[] ReturnStack (8 bytes) | - |-----------------------------------------------------------------------| - | 16-23: Int64 k__BackingField (8 bytes) | - |-----------------------------------------------------------------------| - | 24-31: Int64 k__BackingField (8 bytes) | - |-----------------------------------------------------------------------| - | 32-39: Int64 k__BackingField (8 bytes) | - |-----------------------------------------------------------------------| - | 40-47: Int64 k__BackingField (8 bytes) | - |-----------------------------------------------------------------------| - | 48-51: Int32 DataStackHead (4 bytes) | - |-----------------------------------------------------------------------| - | 52-55: Int32 ReturnStackHead (4 bytes) | - |-----------------------------------------------------------------------| - | 56-59: Int32 k__BackingField (4 bytes) | - |-----------------------------------------------------------------------| - | 60-63: Int32 k__BackingField (4 bytes) | - |-----------------------------------------------------------------------| - | 64: ExecutionType k__BackingField (1 byte) | - |-----------------------------------------------------------------------| - | 65: Boolean k__BackingField (1 byte) | - |-----------------------------------------------------------------------| - | 66: Boolean _canRestore (1 byte) | - |-----------------------------------------------------------------------| - | 67: Boolean k__BackingField (1 byte) | - |-----------------------------------------------------------------------| - | 68: Boolean k__BackingField (1 byte) | - |-----------------------------------------------------------------------| - | 69: Boolean k__BackingField (1 byte) | - |-----------------------------------------------------------------------| - | 70: Boolean _isDisposed (1 byte) | - |-----------------------------------------------------------------------| - | 71: padding (1 byte) | - |-----------------------------------------------------------------------| - | 72-103: EvmPooledMemory _memory (32 bytes) | - |-----------------------------------------------------------------------| - | 104-111: ExecutionEnvironment _env (8 bytes) | - |-----------------------------------------------------------------------| - | 112-143: StackAccessTracker _accessTracker (32 bytes) | - |-----------------------------------------------------------------------| - | 144-155: Snapshot _snapshot (12 bytes) | - |-----------------------------------------------------------------------| - | 156-159: padding (4 bytes) | - |=======================================================================| - */ - public byte[]? DataStack; - public ReturnState[]? ReturnStack; public TGasPolicy Gas; public long InitialStateReservoir; internal long OutputDestination { get; private set; } // TODO: move to CallEnv internal long OutputLength { get; private set; } // TODO: move to CallEnv public long Refund { get; set; } public int DataStackHead; - public int ReturnStackHead; public ExecutionType ExecutionType { get; private set; } // TODO: move to CallEnv public int ProgramCounter { get; set; } - public int FunctionIndex { get; set; } public bool IsTopLevel { get; private set; } // TODO: move to CallEnv private bool _canRestore; public bool IsStatic { get; private set; } // TODO: move to CallEnv @@ -188,9 +129,7 @@ private void Initialize( OutputLength = outputLength; Refund = 0; DataStackHead = 0; - ReturnStackHead = 0; ProgramCounter = 0; - FunctionIndex = 0; ExecutionType = executionType; IsTopLevel = isTopLevel; _canRestore = !isTopLevel; @@ -241,9 +180,8 @@ public void Dispose() if (DataStack is not null) { // Only return if initialized - _stackPool.ReturnStacks(DataStack, ReturnStack!); + _stackPool.ReturnStacks(DataStack); DataStack = null; - ReturnStack = null; } if (_canRestore) @@ -283,7 +221,7 @@ public void InitializeStacks() ObjectDisposedException.ThrowIf(_isDisposed, this); if (DataStack is null) { - (DataStack, ReturnStack) = _stackPool.RentStacks(); + DataStack = _stackPool.RentStacks(); } } @@ -294,14 +232,3 @@ public void CommitToParent(VmState parentState) _canRestore = false; // we can't restore if we committed } } - -/// -/// Return state for EVM call stack management. -/// -public struct ReturnState -{ - public int Index; - public int Offset; - public int Height; -} - diff --git a/src/Nethermind/Nethermind.Runner/pgo/nethermind.mibc b/src/Nethermind/Nethermind.Runner/pgo/nethermind.mibc index a56e824c78fa..28d082e3b3d5 100644 Binary files a/src/Nethermind/Nethermind.Runner/pgo/nethermind.mibc and b/src/Nethermind/Nethermind.Runner/pgo/nethermind.mibc differ diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs index 067c91274b74..f9f8d1f4576e 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs @@ -38,7 +38,6 @@ public void ChainParameters_should_be_loaded_from_chainSpecParamsJson() "MaxCodeSizeTransitionTimestamp", "Eip4844FeeCollectorTransitionTimestamp", "Eip6110TransitionTimestamp", - "Eip7692TransitionTimestamp", "Eip7928TransitionTimestamp", // todo: remove when added to chainspec "Eip7843TransitionTimestamp" ]; diff --git a/src/Nethermind/Nethermind.Specs.Test/MainnetSpecProviderTests.cs b/src/Nethermind/Nethermind.Specs.Test/MainnetSpecProviderTests.cs index c1a5ad98842c..d8d70f6aea56 100644 --- a/src/Nethermind/Nethermind.Specs.Test/MainnetSpecProviderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/MainnetSpecProviderTests.cs @@ -69,8 +69,6 @@ public void Prague_eips(long blockNumber, ulong timestamp, bool isEnabled) { _specProvider.GetSpec(new ForkActivation(blockNumber, timestamp)).Eip2935ContractAddress.Should().BeNull(); } - // EOF was once in Prague but moved to Osaka, so let's verify it's not enabled. - _specProvider.GetSpec(new ForkActivation(blockNumber, timestamp)).IsEofEnabled.Should().Be(false); } [TestCase(MainnetSpecProvider.ParisBlockNumber, MainnetSpecProvider.PragueBlockTimestamp, false)] diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 5452a27fd4d3..4b19e725b5f7 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -112,7 +112,6 @@ public class OverridableReleaseSpec(IReleaseSpec spec) : IReleaseSpec public UInt256 BaseFeeMaxChangeDenominator { get; set; } = spec.BaseFeeMaxChangeDenominator; public long ElasticityMultiplier { get; set; } = spec.ElasticityMultiplier; public IBaseFeeCalculator BaseFeeCalculator { get; set; } = spec.BaseFeeCalculator; - public bool IsEofEnabled { get; set; } = spec.IsEofEnabled; public bool IsEip8024Enabled { get; set; } = spec.IsEip8024Enabled; public bool IsEip6110Enabled { get; set; } = spec.IsEip6110Enabled; public Address? DepositContractAddress { get; set; } = spec.DepositContractAddress; diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs index 68b3c0389da0..ad5cb8973495 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs @@ -132,7 +132,6 @@ public class ChainParameters public long Eip2935RingBufferSize { get; set; } = Eip2935Constants.RingBufferSize; public ulong? Eip7951TransitionTimestamp { get; set; } public ulong? Rip7212TransitionTimestamp { get; set; } - public ulong? Eip7692TransitionTimestamp { get; set; } public ulong? Eip7702TransitionTimestamp { get; set; } public ulong? OpGraniteTransitionTimestamp { get; set; } public ulong? OpHoloceneTransitionTimestamp { get; set; } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 8982eeb3469d..57dedfa5819e 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -255,7 +255,6 @@ protected virtual ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releas releaseSpec.IsEip4788Enabled = (chainSpec.Parameters.Eip4788TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.Eip4788ContractAddress = chainSpec.Parameters.Eip4788ContractAddress; releaseSpec.IsEip2935Enabled = (chainSpec.Parameters.Eip2935TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; - releaseSpec.IsEofEnabled = (chainSpec.Parameters.Eip7692TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.Eip2935ContractAddress = chainSpec.Parameters.Eip2935ContractAddress; releaseSpec.Eip2935RingBufferSize = chainSpec.Parameters.Eip2935RingBufferSize; diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index 073d41ff0aa2..c1d1304cc9c5 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -149,7 +149,6 @@ bool GetForInnerPathExistence(KeyValuePair o) => Eip6780TransitionTimestamp = chainSpecJson.Params.Eip6780TransitionTimestamp, Eip7951TransitionTimestamp = chainSpecJson.Params.Eip7951TransitionTimestamp, Rip7212TransitionTimestamp = chainSpecJson.Params.Rip7212TransitionTimestamp, - Eip7692TransitionTimestamp = chainSpecJson.Params.Eip7692TransitionTimestamp, OpGraniteTransitionTimestamp = chainSpecJson.Params.OpGraniteTransitionTimestamp, OpHoloceneTransitionTimestamp = chainSpecJson.Params.OpHoloceneTransitionTimestamp, OpIsthmusTransitionTimestamp = chainSpecJson.Params.OpIsthmusTransitionTimestamp, diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs index 538ca967971c..f6ec534c02c6 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs @@ -159,7 +159,6 @@ public class ChainSpecParamsJson public Address Eip7251ContractAddress { get; set; } public ulong? Eip7951TransitionTimestamp { get; set; } public ulong? Rip7212TransitionTimestamp { get; set; } - public ulong? Eip7692TransitionTimestamp { get; set; } public ulong? Eip7702TransitionTimestamp { get; set; } public ulong? Eip7883TransitionTimestamp { get; set; } public ulong? Eip7823TransitionTimestamp { get; set; } diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index d65c259ed525..20a846f01379 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -111,7 +111,6 @@ public class ReleaseSpec : IReleaseSpec public Address? Eip7002ContractAddress { get => IsEip7002Enabled ? field : null; set; } [MemberNotNullWhen(true, nameof(IsEip4788Enabled))] public Address? Eip4788ContractAddress { get => IsEip4788Enabled ? field : null; set; } - public bool IsEofEnabled { get; set; } public bool IsEip8024Enabled { get; set; } public bool IsEip6110Enabled { get; set; } [MemberNotNullWhen(true, nameof(IsEip6110Enabled))] diff --git a/src/Nethermind/Nethermind.Specs/SpecNameParser.cs b/src/Nethermind/Nethermind.Specs/SpecNameParser.cs index a4030e1274ca..dc3964bcf9cd 100644 --- a/src/Nethermind/Nethermind.Specs/SpecNameParser.cs +++ b/src/Nethermind/Nethermind.Specs/SpecNameParser.cs @@ -18,11 +18,8 @@ public static IReleaseSpec Parse(string specName) .Replace("DAO", "Dao") .Replace("Merged", "Paris") .Replace("Merge", "Paris") - .Replace("London+3540+3670", "Shanghai") - .Replace("GrayGlacier+3540+3670", "Shanghai") .Replace("GrayGlacier+3860", "Shanghai") .Replace("GrayGlacier+3855", "Shanghai") - .Replace("Merge+3540+3670", "Shanghai") .Replace("Shanghai+3855", "Shanghai") .Replace("Shanghai+3860", "Shanghai") .Replace("GrayGlacier+1153", "Cancun") diff --git a/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs b/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs deleted file mode 100644 index 8a4a283b4510..000000000000 --- a/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using Ethereum.Test.Base; - -namespace Nethermind.Test.Runner; - -public class EofTestsRunner(ITestSourceLoader testsSource, string? filter) : EofTestBase, IEofTestRunner -{ - private readonly ConsoleColor _defaultColour = Console.ForegroundColor; - private readonly ITestSourceLoader _testsSource = testsSource ?? throw new ArgumentNullException(nameof(testsSource)); - - public IEnumerable RunTests() - { - List testResults = new(); - var tests = _testsSource.LoadTests(); - foreach (EofTest test in tests) - { - if (filter is not null && !Regex.Match(test.Name, $"^({filter})").Success) - continue; - Setup(); - - Console.Write($"{test.Name,-120} "); - if (test.LoadFailure is not null) - { - WriteRed(test.LoadFailure); - testResults.Add(new EthereumTestResult(test.Name, test.LoadFailure)); - } - else - { - var result = new EthereumTestResult(test.Name, "Osaka", RunTest(test)); - testResults.Add(result); - if (result.Pass) - WriteGreen("PASS"); - else - WriteRed("FAIL"); - } - } - - return testResults; - } - - private void WriteRed(string text) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(text); - Console.ForegroundColor = _defaultColour; - } - - private void WriteGreen(string text) - { - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(text); - Console.ForegroundColor = _defaultColour; - } -} diff --git a/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj b/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj index 85f90c38fdcd..a6458294744f 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj +++ b/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj @@ -26,9 +26,6 @@ PreserveNewest - - PreserveNewest - Always diff --git a/src/Nethermind/Nethermind.Test.Runner/Program.cs b/src/Nethermind/Nethermind.Test.Runner/Program.cs index da5fdf2c0f4c..09e8acdada7a 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Test.Runner/Program.cs @@ -24,8 +24,6 @@ public class Options public static Option BlockTest { get; } = new("--blockTest", "-b") { Description = "Set test as blockTest. if not, it will be by default assumed a state test." }; - public static Option EofTest { get; } = - new("--eofTest", "-e") { Description = "Set test as eofTest. if not, it will be by default assumed a state test." }; public static Option TraceAlways { get; } = new("--trace", "-t") { Description = "Set to always trace (by default traces are only generated for failing tests)." }; @@ -58,7 +56,6 @@ public static async Task Main(params string[] args) Options.Input, Options.Filter, Options.BlockTest, - Options.EofTest, Options.TraceAlways, Options.TraceNever, Options.ExcludeMemory, @@ -102,12 +99,6 @@ private static async Task Run(ParseResult parseResult, CancellationToken ca !parseResult.GetValue(Options.ExcludeMemory), parseResult.GetValue(Options.ExcludeStack))); } - else if (parseResult.GetValue(Options.EofTest)) - { - RunEofTest(input, source => new EofTestsRunner( - source, - parseResult.GetValue(Options.Filter))); - } else { RunStateTest(input, source => new StateTestsRunner( @@ -141,14 +132,6 @@ private static async Task RunBlockTest(string path, Func testRunnerBuilder) - { - ITestSourceLoader source = Path.HasExtension(path) - ? new TestsSourceLoader(new LoadEofTestFileStrategy(), path) - : new TestsSourceLoader(new LoadEofTestsStrategy(), path); - testRunnerBuilder(source).RunTests(); - } - private static void RunStateTest(string path, Func testRunnerBuilder) { ITestSourceLoader source = Path.HasExtension(path) diff --git a/src/Nethermind/Nethermind.Test.Runner/Properties/launchSettings.json b/src/Nethermind/Nethermind.Test.Runner/Properties/launchSettings.json index beae8f8be970..00a240af8c7b 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Properties/launchSettings.json +++ b/src/Nethermind/Nethermind.Test.Runner/Properties/launchSettings.json @@ -12,10 +12,6 @@ "commandName": "Project", "commandLineArgs": "-b -i blockchainTest1.json" }, - "EOF Test": { - "commandName": "Project", - "commandLineArgs": "-e -i eoftest1.json" - }, "Gnosis Blockchain Test": { "commandName": "Project", "commandLineArgs": "-g -b -i gnosisBlockchainTest1.json" diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs index 4d98b865b4db..749925c98f10 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs @@ -10,7 +10,6 @@ using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.Evm.TransactionProcessing; -using Nethermind.Evm.CodeAnalysis; namespace Nethermind.Test.Runner; @@ -51,18 +50,16 @@ public void MarkAsFailed(Address recipient, in GasConsumed gasSpent, byte[] outp _trace.Result.GasUsed = gasSpent; } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { _gasAlreadySetForCurrentOp = false; _traceEntry = new StateTestTxTraceEntry { - Pc = pc + (env.CodeInfo is EofCodeInfo eofCodeInfo ? eofCodeInfo.PcOffset() : 0), - Section = codeSection, + Pc = pc, Operation = (byte)opcode, - OperationName = opcode.GetName(), + OperationName = Enum.GetName(opcode), Gas = gas, Depth = env.GetGethTraceDepth(), - FunctionDepth = functionDepth }; _trace.Entries.Add(_traceEntry); } diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs b/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs index a73283ba3d36..1d67ce581877 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs @@ -15,9 +15,6 @@ public StateTestTxTraceEntry() public int Pc { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public int Section { get; set; } - [JsonPropertyName("op")] public byte Operation { get; set; } @@ -33,9 +30,6 @@ public StateTestTxTraceEntry() public int Depth { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public int FunctionDepth { get; set; } - public int Refund { get; set; } [JsonPropertyName("opName")] diff --git a/src/Nethermind/Nethermind.Test.Runner/eoftest1.json b/src/Nethermind/Nethermind.Test.Runner/eoftest1.json deleted file mode 100644 index b9688e00a418..000000000000 --- a/src/Nethermind/Nethermind.Test.Runner/eoftest1.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "tests/prague/eip7692_eof_v1/eip3540_eof_v1/test_eof_example.py::test_eof_example[fork_CancunEIP7692-eof_test]": { - "vectors": { - "0": { - "code": "0xef0001010010020004000500060008000204000100008000010100000100010003020300035fe300010050e3000250e43080e300035050e480e4ef", - "results": { - "Prague": { - "result": true - } - } - } - }, - "_info": { - "hash": "0xf91c0d32c0e59772417a3e223b57590c91593cb17369253549b946f971c5fcbf", - "comment": "`execution-spec-tests` generated test", - "filling-transition-tool": "evmone-t8n 0.12.0-6+commit.2d20cc63.dirty", - "description": "Test function documentation:\n\n Example of python EOF classes", - "url": "https://github.com/ethereum/execution-spec-tests/blob/99d4c17de5888bb5343c767ef34b2735d2469770/tests/prague/eip7692_eof_v1/eip3540_eof_v1/test_eof_example.py#L19", - "reference-spec": "https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3540.md", - "reference-spec-version": "8dcb0a8c1c0102c87224308028632cc986a61183" - } - } -}