From ed3f6f5a7079257e98528c5f75179a79833d1d53 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 16 Jan 2024 17:25:43 +0100 Subject: [PATCH 001/255] update release spec --- .../Nethermind.Core/Specs/IReleaseSpec.cs | 5 +++++ .../OverridableReleaseSpec.cs | 2 ++ .../Nethermind.Specs/Forks/17_Prague.cs | 21 +++++++++++++++++++ .../Nethermind.Specs/ReleaseSpec.cs | 2 ++ .../SystemTransactionReleaseSpec.cs | 2 ++ 5 files changed, 32 insertions(+) create mode 100644 src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index f7e8519de72..5c62d41c09f 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -278,6 +278,11 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec /// bool IsEip6780Enabled { get; } + /// + /// Eof execution env in EVM + /// + bool IsEofEnabled { get; } + /// /// Should transactions be validated against chainId. /// diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index b36a57071d0..213b91777db 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -160,5 +160,7 @@ public ulong Eip4844TransitionTimestamp public UInt256 ForkBaseFee => _spec.ForkBaseFee; public UInt256 BaseFeeMaxChangeDenominator => _spec.BaseFeeMaxChangeDenominator; public long ElasticityMultiplier => _spec.ElasticityMultiplier; + + public bool IsEofEnabled => _spec.IsEofEnabled; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs b/src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs new file mode 100644 index 00000000000..88dc67646e4 --- /dev/null +++ b/src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading; +using Nethermind.Core; +using Nethermind.Core.Specs; + +namespace Nethermind.Specs.Forks; + +public class Prague : Shanghai +{ + private static IReleaseSpec _instance; + + protected Prague() + { + Name = "Prague"; + IsEofEnabled = true; + } + + public new static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new Prague()); +} diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index 5de83c8356b..c7a4527b434 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -91,5 +91,7 @@ public Address Eip4788ContractAddress get => IsEip4788Enabled ? _eip4788ContractAddress : null; set => _eip4788ContractAddress = value; } + + public bool IsEofEnabled { get; set; } } } diff --git a/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs index 8774d7a03ae..ec5feaeb465 100644 --- a/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs @@ -133,5 +133,7 @@ public bool IsEip158IgnoredAccount(Address address) public UInt256 ForkBaseFee => _spec.ForkBaseFee; public UInt256 BaseFeeMaxChangeDenominator => _spec.BaseFeeMaxChangeDenominator; public long ElasticityMultiplier => _spec.ElasticityMultiplier; + + public bool IsEofEnabled => _spec.IsEofEnabled; } } From 9e16b0b04509be0fe76bd9fb594c36f13f35dbed Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jan 2024 00:04:16 +0100 Subject: [PATCH 002/255] added new transaction type for eof initcode --- src/Nethermind/Nethermind.Core/Transaction.cs | 13 ++++ src/Nethermind/Nethermind.Core/TxType.cs | 1 + .../Nethermind.Serialization.Rlp/TxDecoder.cs | 68 +++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index f83f0ea5d75..6000a75fc05 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -45,10 +45,12 @@ public class Transaction public bool SupportsAccessList => Type >= TxType.AccessList && Type != TxType.DepositTx; public bool Supports1559 => Type >= TxType.EIP1559 && Type != TxType.DepositTx; public bool SupportsBlobs => Type == TxType.Blob && Type != TxType.DepositTx; + public bool SupportsEofInitcode => Type == TxType.EofInitcodeTx && Type != TxType.DepositTx; public long GasLimit { get; set; } public Address? To { get; set; } public UInt256 Value { get; set; } public Memory? Data { get; set; } + public byte[][]? Initcodes { get; set; } public Address? SenderAddress { get; set; } public Signature? Signature { get; set; } public bool IsSigned => Signature is not null; @@ -224,6 +226,16 @@ public string ToString(string indent) builder.AppendLine($"{indent}{nameof(BlobVersionedHashes)}: {BlobVersionedHashes?.Length}"); } + if (SupportsEofInitcode) + { + builder.AppendLine($"{indent}{nameof(Initcodes)}: ["); + foreach (var initcode in Initcodes!) + { + builder.AppendLine($"{indent}{indent}{initcode.ToHexString()}"); + } + builder.AppendLine($"]"); + } + return builder.ToString(); } @@ -261,6 +273,7 @@ public bool Return(Transaction obj) obj.NetworkWrapper = default; obj.IsServiceTransaction = default; obj.PoolIndex = default; + obj.Initcodes = default; obj._size = default; return true; diff --git a/src/Nethermind/Nethermind.Core/TxType.cs b/src/Nethermind/Nethermind.Core/TxType.cs index 9257754dab6..c0801626c16 100644 --- a/src/Nethermind/Nethermind.Core/TxType.cs +++ b/src/Nethermind/Nethermind.Core/TxType.cs @@ -9,6 +9,7 @@ public enum TxType : byte AccessList = 1, EIP1559 = 2, Blob = 3, + EofInitcodeTx = 4, DepositTx = 0x7E, } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs index e456d5a7fd8..367f261d84b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs @@ -91,6 +91,9 @@ protected virtual T NewTx() case TxType.Blob: DecodeShardBlobPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); break; + case TxType.EofInitcodeTx: + DecodeEofPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); + break; case TxType.DepositTx: TxDecoder.DecodeDepositPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); break; @@ -210,6 +213,20 @@ private void DecodeEip1559PayloadWithoutSig(T transaction, RlpStream rlpStream, transaction.AccessList = _accessListDecoder.Decode(rlpStream, rlpBehaviors); } + private void DecodeEofPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + transaction.ChainId = rlpStream.DecodeULong(); + transaction.Nonce = rlpStream.DecodeUInt256(); + transaction.GasPrice = rlpStream.DecodeUInt256(); // gas premium + transaction.DecodedMaxFeePerGas = rlpStream.DecodeUInt256(); + transaction.GasLimit = rlpStream.DecodeLong(); + transaction.To = rlpStream.DecodeAddress(); + transaction.Value = rlpStream.DecodeUInt256(); + transaction.Data = rlpStream.DecodeByteArray(); + transaction.AccessList = _accessListDecoder.Decode(rlpStream, rlpBehaviors); + transaction.Initcodes = rlpStream.DecodeByteArrays(); + } + private void DecodeShardBlobPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) { transaction.ChainId = rlpStream.DecodeULong(); @@ -279,6 +296,20 @@ private void DecodeEip1559PayloadWithoutSig(T transaction, ref Rlp.ValueDecoderC transaction.Data = decoderContext.DecodeByteArrayMemory(); transaction.AccessList = _accessListDecoder.Decode(ref decoderContext, rlpBehaviors); } + private void DecodeEofPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) + { + transaction.ChainId = decoderContext.DecodeULong(); + transaction.Nonce = decoderContext.DecodeUInt256(); + transaction.GasPrice = decoderContext.DecodeUInt256(); // gas premium + transaction.DecodedMaxFeePerGas = decoderContext.DecodeUInt256(); + transaction.GasLimit = decoderContext.DecodeLong(); + transaction.To = decoderContext.DecodeAddress(); + transaction.Value = decoderContext.DecodeUInt256(); + transaction.Data = decoderContext.DecodeByteArrayMemory(); + transaction.AccessList = _accessListDecoder.Decode(ref decoderContext, rlpBehaviors); + transaction.Initcodes = decoderContext.DecodeByteArrays(); + } + private void DecodeShardBlobPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) { @@ -350,6 +381,20 @@ private void EncodeEip1559PayloadWithoutPayload(T item, RlpStream stream, RlpBeh _accessListDecoder.Encode(stream, item.AccessList, rlpBehaviors); } + private void EncodeEofPayloadWithoutPayload(T item, RlpStream stream, RlpBehaviors rlpBehaviors) + { + stream.Encode(item.ChainId ?? 0); + stream.Encode(item.Nonce); + stream.Encode(item.GasPrice); // gas premium + stream.Encode(item.DecodedMaxFeePerGas); + stream.Encode(item.GasLimit); + stream.Encode(item.To); + stream.Encode(item.Value); + stream.Encode(item.Data); + _accessListDecoder.Encode(stream, item.AccessList, rlpBehaviors); + stream.Encode(item.Initcodes); + } + private void EncodeShardBlobPayloadWithoutPayload(T item, RlpStream stream, RlpBehaviors rlpBehaviors) { stream.Encode(item.ChainId ?? 0); @@ -453,6 +498,9 @@ public void Decode(ref Rlp.ValueDecoderContext decoderContext, ref T? transactio case TxType.EIP1559: DecodeEip1559PayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); break; + case TxType.EofInitcodeTx: + DecodeEofPayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); + break; case TxType.Blob: DecodeShardBlobPayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); break; @@ -653,6 +701,9 @@ private void EncodeTx(RlpStream stream, T? item, RlpBehaviors rlpBehaviors = Rlp case TxType.Blob: EncodeShardBlobPayloadWithoutPayload(item, stream, rlpBehaviors); break; + case TxType.EofInitcodeTx: + EncodeEofPayloadWithoutPayload(item, stream, rlpBehaviors); + break; case TxType.DepositTx: TxDecoder.EncodeDepositTxPayloadWithoutPayload(item, stream); break; @@ -746,6 +797,20 @@ private int GetEip1559ContentLength(T item) + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None); } + private int GetEofContentLength(T item) + { + return Rlp.LengthOf(item.Nonce) + + Rlp.LengthOf(item.GasPrice) // gas premium + + Rlp.LengthOf(item.DecodedMaxFeePerGas) + + Rlp.LengthOf(item.GasLimit) + + Rlp.LengthOf(item.To) + + Rlp.LengthOf(item.Value) + + Rlp.LengthOf(item.Data) + + Rlp.LengthOf(item.ChainId ?? 0) + + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None) + + Rlp.LengthOf(item.Initcodes); + } + private int GetShardBlobContentLength(T item) { return Rlp.LengthOf(item.Nonce) @@ -801,6 +866,9 @@ private int GetContentLength(T item, bool forSigning, bool isEip155Enabled = fal case TxType.Blob: contentLength = GetShardBlobContentLength(item); break; + case TxType.EofInitcodeTx: + contentLength = GetEofContentLength(item); + break; case TxType.DepositTx: contentLength = TxDecoder.GetDepositTxContentLength(item); break; From 20f30032d67116bef6f3cfb5eb76e1b2662399c1 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jan 2024 02:09:17 +0100 Subject: [PATCH 003/255] Added new opcodes --- src/Nethermind/Nethermind.Evm/Instruction.cs | 181 ++++++++++++++++++- 1 file changed, 177 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index ab43e5c3888..8a844f6e7e0 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -1,9 +1,12 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Diagnostics.CodeAnalysis; using FastEnumUtility; using Nethermind.Core.Specs; +using Nethermind.Evm.EOF; +using Nethermind.Specs.Forks; namespace Nethermind.Evm { @@ -174,18 +177,188 @@ public enum Instruction : byte REVERT = 0xfd, INVALID = 0xfe, SELFDESTRUCT = 0xff, - } + RJUMP = 0xe0, + RJUMPI = 0xe1, + RJUMPV = 0xe2, + CALLF = 0xe3, + RETF = 0xe4, + JUMPF = 0xe5, + CREATE3 = 0xec, + CREATE4 = 0xed, + RETURNCONTRACT = 0xee, + DATALOAD = 0xe8, + DATALOADN = 0xe9, + DATASIZE = 0xea, + DATACOPY = 0xeb, + + DUPN = 0xe6, + SWAPN = 0xe7, + EXCHANGE = 0xf8, + RETURNDATALOAD = 0xf7 + + } public static class InstructionExtensions { - public static string? GetName(this Instruction instruction, bool isPostMerge = false, IReleaseSpec? spec = null) => - instruction switch + public static int GetImmediateCount(this Instruction instruction, bool IsEofContext, byte jumpvCount = 0) + => instruction switch + { + Instruction.CALLF or Instruction.JUMPF => IsEofContext ? EvmObjectFormat.TWO_BYTE_LENGTH : 0, + Instruction.DUPN or Instruction.SWAPN => IsEofContext ? EvmObjectFormat.ONE_BYTE_LENGTH : 0, + Instruction.RJUMP or Instruction.RJUMPI => IsEofContext ? EvmObjectFormat.TWO_BYTE_LENGTH : 0, + Instruction.RJUMPV => IsEofContext ? jumpvCount * EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.ONE_BYTE_LENGTH : 0, + >= Instruction.PUSH0 and <= Instruction.PUSH32 => instruction - Instruction.PUSH0, + _ => 0 + }; + public static bool IsTerminating(this Instruction instruction) => instruction switch + { + Instruction.RETF or Instruction.INVALID or Instruction.STOP or Instruction.RETURN or Instruction.REVERT => true, + Instruction.JUMPF or Instruction.RETURNCONTRACT => true, + // Instruction.SELFDESTRUCT => true + _ => false + }; + + public static bool IsValid(this Instruction instruction, bool IsEofContext) + { + if (!Enum.IsDefined(instruction)) + { + return false; + } + + return instruction switch + { + Instruction.CREATE2 or Instruction.CREATE => !IsEofContext, + Instruction.EXTCODEHASH or Instruction.EXTCODECOPY or Instruction.EXTCODESIZE => !IsEofContext, + Instruction.CODECOPY or Instruction.CODESIZE => !IsEofContext, + Instruction.GAS => !IsEofContext, + Instruction.PC => !IsEofContext, + Instruction.CALLCODE or Instruction.SELFDESTRUCT => !IsEofContext, + Instruction.JUMPI or Instruction.JUMP => !IsEofContext, + Instruction.CALLF or Instruction.RETF or Instruction.JUMPF => IsEofContext, + Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE => IsEofContext, + Instruction.BEGINSUB or Instruction.RETURNSUB or Instruction.JUMPSUB => true, + Instruction.CALL or Instruction.DELEGATECALL or Instruction.GAS => !IsEofContext, + _ => true + }; + } + + //Note() : Extensively test this, refactor it, + public static (ushort? InputCount, ushort? OutputCount, ushort? immediates) StackRequirements(this Instruction instruction) => instruction switch + { + Instruction.STOP => (0, 0, 0), + Instruction.ADD => (2, 1, 0), + Instruction.MUL => (2, 1, 0), + Instruction.SUB => (2, 1, 0), + Instruction.DIV => (2, 1, 0), + Instruction.SDIV => (2, 1, 0), + Instruction.MOD => (2, 1, 0), + Instruction.SMOD => (2, 1, 0), + Instruction.ADDMOD => (3, 1, 0), + Instruction.MULMOD => (3, 1, 0), + Instruction.EXP => (2, 1, 0), + Instruction.SIGNEXTEND => (2, 1, 0), + Instruction.LT => (2, 1, 0), + Instruction.GT => (2, 1, 0), + Instruction.SLT => (2, 1, 0), + Instruction.SGT => (2, 1, 0), + Instruction.EQ => (2, 1, 0), + Instruction.ISZERO => (1, 1, 0), + Instruction.AND => (2, 1, 0), + Instruction.OR => (2, 1, 0), + Instruction.XOR => (2, 1, 0), + Instruction.NOT => (1, 1, 0), + Instruction.BYTE => (2, 1, 0), + Instruction.SHL => (2, 1, 0), + Instruction.SHR => (2, 1, 0), + Instruction.SAR => (2, 1, 0), + Instruction.KECCAK256 => (2, 1, 0), + Instruction.ADDRESS => (0, 1, 0), + Instruction.BALANCE => (1, 1, 0), + Instruction.ORIGIN => (0, 1, 0), + Instruction.CALLER => (0, 1, 0), + Instruction.CALLVALUE => (0, 1, 0), + Instruction.CALLDATALOAD => (1, 1, 0), + Instruction.CALLDATASIZE => (0, 1, 0), + Instruction.CALLDATACOPY => (3, 0, 0), + Instruction.CODESIZE => (0, 1, 0), + Instruction.CODECOPY => (3, 0, 0), + Instruction.GASPRICE => (0, 1, 0), + Instruction.EXTCODESIZE => (1, 1, 0), + Instruction.EXTCODECOPY => (4, 0, 0), + Instruction.RETURNDATASIZE => (0, 1, 0), + Instruction.RETURNDATACOPY => (3, 0, 0), + Instruction.EXTCODEHASH => (1, 1, 0), + Instruction.BLOCKHASH => (1, 1, 0), + Instruction.COINBASE => (0, 1, 0), + Instruction.TIMESTAMP => (0, 1, 0), + Instruction.NUMBER => (0, 1, 0), + Instruction.PREVRANDAO => (0, 1, 0), + Instruction.GASLIMIT => (0, 1, 0), + Instruction.CHAINID => (0, 1, 0), + Instruction.SELFBALANCE => (0, 1, 0), + Instruction.BASEFEE => (0, 1, 0), + Instruction.POP => (1, 0, 0), + Instruction.MLOAD => (1, 1, 0), + Instruction.MSTORE => (2, 0, 0), + Instruction.MSTORE8 => (2, 0, 0), + Instruction.SLOAD => (1, 1, 0), + Instruction.SSTORE => (2, 0, 0), + Instruction.MSIZE => (0, 1, 0), + Instruction.GAS => (0, 1, 0), + Instruction.JUMPDEST => (0, 0, 0), + Instruction.RJUMP => (0, 0, 2), + Instruction.RJUMPI => (1, 0, 2), + Instruction.BLOBHASH => (1, 1, 0), + >= Instruction.PUSH0 and <= Instruction.PUSH32 => (0, 1, 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.LOG0 => (2, 0, 0), + Instruction.LOG1 => (3, 0, 0), + Instruction.LOG2 => (4, 0, 0), + Instruction.LOG3 => (5, 0, 0), + Instruction.LOG4 => (6, 0, 0), + Instruction.CALLF => (0, 0, 2), + Instruction.JUMPF => (0, 0, 2), + Instruction.RETF => (0, 0, 0), + Instruction.CREATE => (3, 1, 0), + Instruction.CALL => (6, 1, 0), + Instruction.RETURN => (2, 0, 0), + Instruction.DELEGATECALL => (6, 1, 0), + Instruction.CREATE2 => (4, 1, 0), + Instruction.STATICCALL => (6, 1, 0), + Instruction.REVERT => (2, 0, 0), + Instruction.INVALID => (0, 0, 0), + + Instruction.CREATE3 => (4, 1, 1), + Instruction.CREATE4 => (5, 1, 0), + Instruction.RETURNCONTRACT => (2, 2, 1), + Instruction.DATALOAD => (1, 1, 0), + Instruction.DATALOADN => (0, 1, 1), + Instruction.DATASIZE => (0, 1, 0), + Instruction.DATACOPY => (3, 1, 0), + + Instruction.RJUMPV => (1, 0, null), // null indicates this is a dynamic multi-bytes opcode + Instruction.SWAPN => (null, null, 1), + Instruction.DUPN => (null, null, 1), + Instruction.EXCHANGE => (null, null, 1), + _ => throw new NotImplementedException($"Instruction {instruction} not implemented") + }; + + public static string? GetName(this Instruction instruction, bool isPostMerge = false, IReleaseSpec? spec = null) + { + spec ??= Frontier.Instance; + return instruction switch { Instruction.PREVRANDAO when !isPostMerge => "DIFFICULTY", + Instruction.RJUMP => spec.IsEofEnabled ? "RJUMP" : "BEGINSUB", + Instruction.RJUMPI => spec.IsEofEnabled ? "RJUMPI" : "RETURNSUB", + Instruction.RJUMPV => spec.IsEofEnabled ? "RJUMPV" : "JUMPSUB", Instruction.TLOAD or Instruction.BEGINSUB => spec?.TransientStorageEnabled == true ? "TLOAD" : "BEGINSUB", Instruction.TSTORE or Instruction.RETURNSUB => spec?.TransientStorageEnabled == true ? "TSTORE" : "RETURNSUB", Instruction.JUMPSUB or Instruction.MCOPY => spec?.IsEip5656Enabled == true ? "MCOPY" : "JUMPSUB", - _ => FastEnum.IsDefined(instruction) ? FastEnum.GetName(instruction) : null + Instruction.JUMPDEST => spec.IsEofEnabled ? "NOP" : "JUMPDEST", + _ => FastEnum.IsDefined(instruction) ? FastEnum.GetName(instruction) : null, }; + } } } From fb96c12c6e393e0927aa4172cefe62aca1542abf Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jan 2024 03:07:50 +0100 Subject: [PATCH 004/255] migrate validation code from old PR add exchange validation rule --- .../Nethermind.Core/Extensions/Bytes.cs | 56 ++ src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 190 ++++ .../EvmObjectFormat/EofCodeValidator.cs | 864 ++++++++++++++++++ .../EvmObjectFormat/EofHeader.cs | 36 + 4 files changed, 1146 insertions(+) create mode 100644 src/Nethermind/Nethermind.Evm/BitmapHelper.cs create mode 100644 src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs create mode 100644 src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index c772a39b766..6941d471606 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -365,6 +365,62 @@ public static BigInteger ToUnsignedBigInteger(this ReadOnlySpan bytes) { return new(bytes, true, true); } + public static short ReadEthInt16(this Span bytes) + { + return ReadEthInt16((ReadOnlySpan)bytes); + } + + public static short ReadEthInt16(this ReadOnlySpan bytes) + { + if (bytes.Length > 2) + { + bytes = bytes.Slice(bytes.Length - 2, 2); + } + + return bytes.Length switch + { + 2 => BinaryPrimitives.ReadInt16BigEndian(bytes), + 1 => bytes[0], + _ => 0 + }; + } + + public static ushort ReadEthUInt16(this Span bytes) + { + return ReadEthUInt16((ReadOnlySpan)bytes); + } + + public static ushort ReadEthUInt16(this ReadOnlySpan bytes) + { + if (bytes.Length > 2) + { + bytes = bytes.Slice(bytes.Length - 2, 2); + } + + return bytes.Length switch + { + 2 => BinaryPrimitives.ReadUInt16BigEndian(bytes), + 1 => bytes[0], + _ => 0 + }; + } + + public static ushort ReadEthUInt16LittleEndian(this Span bytes) + { + if (bytes.Length > 2) + { + bytes = bytes.Slice(bytes.Length - 2, 2); + } + + if (bytes.Length == 2) + { + return BinaryPrimitives.ReadUInt16LittleEndian(bytes); + } + + Span fourBytes = stackalloc byte[2]; + bytes.CopyTo(fourBytes[(2 - bytes.Length)..]); + return BinaryPrimitives.ReadUInt16LittleEndian(fourBytes); + } public static uint ReadEthUInt32(this Span bytes) { diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs new file mode 100644 index 00000000000..e86212c1205 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -0,0 +1,190 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace Nethermind.Evm; +public static class BitmapHelper +{ + private const ushort Set2BitsMask = 0b1100_0000_0000_0000; + private const ushort Set3BitsMask = 0b1110_0000_0000_0000; + private const ushort Set4BitsMask = 0b1111_0000_0000_0000; + private const ushort Set5BitsMask = 0b1111_1000_0000_0000; + private const ushort Set6BitsMask = 0b1111_1100_0000_0000; + private const ushort Set7BitsMask = 0b1111_1110_0000_0000; + + private static readonly byte[] _lookup = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; + + /// + /// Collects data locations in code. + /// An unset bit means the byte is an opcode, a set bit means it's data. + /// + public static byte[] CreateCodeBitmap(ReadOnlySpan code, bool isEof = false) + { + // The bitmap is 4 bytes longer than necessary, in case the code + // ends with a PUSH32, the algorithm will push zeroes onto the + // bitvector outside the bounds of the actual code. + byte[] bitvec = new byte[(code.Length / 8) + 1 + 4]; + + for (int pc = 0; pc < code.Length;) + { + Instruction op = (Instruction)code[pc]; + pc++; + + int numbits = op switch + { + Instruction.RJUMPV => isEof ? op.GetImmediateCount(isEof, code[pc]) : 0, + _ => op.GetImmediateCount(isEof), + }; + + if (numbits == 0) continue; + + HandleNumbits(numbits, bitvec, ref pc); + } + return bitvec; + } + + public static void HandleNumbits(int numbits, byte[] bitvec, scoped ref int pc) + { + if (numbits >= 8) + { + for (; numbits >= 16; numbits -= 16) + { + bitvec.Set16(pc); + pc += 16; + } + + for (; numbits >= 8; numbits -= 8) + { + bitvec.Set8(pc); + pc += 8; + } + } + + switch (numbits) + { + case 1: + bitvec.Set1(pc); + pc += 1; + break; + case 2: + bitvec.SetN(pc, Set2BitsMask); + pc += 2; + break; + case 3: + bitvec.SetN(pc, Set3BitsMask); + pc += 3; + break; + case 4: + bitvec.SetN(pc, Set4BitsMask); + pc += 4; + break; + case 5: + bitvec.SetN(pc, Set5BitsMask); + pc += 5; + break; + case 6: + bitvec.SetN(pc, Set6BitsMask); + pc += 6; + break; + case 7: + bitvec.SetN(pc, Set7BitsMask); + pc += 7; + break; + } + } + /// + /// Checks if the position is in a code segment. + /// + public static bool IsCodeSegment(byte[] bitvec, int pos) + { + return (bitvec[pos / 8] & (0x80 >> (pos % 8))) == 0; + } + + private static void Set1(this byte[] bitvec, int pos) + { + bitvec[pos / 8] |= _lookup[pos % 8]; + } + + private static void SetN(this byte[] bitvec, int pos, UInt16 flag) + { + ushort a = (ushort)(flag >> (pos % 8)); + bitvec[pos / 8] |= (byte)(a >> 8); + byte b = (byte)a; + if (b != 0) + { + // If the bit-setting affects the neighbouring byte, we can assign - no need to OR it, + // since it's the first write to that byte + bitvec[pos / 8 + 1] = b; + } + } + + private static void Set8(this byte[] bitvec, int pos) + { + byte a = (byte)(0xFF >> (pos % 8)); + bitvec[pos / 8] |= a; + bitvec[pos / 8 + 1] = (byte)~a; + } + + private static void Set16(this byte[] bitvec, int pos) + { + byte a = (byte)(0xFF >> (pos % 8)); + bitvec[pos / 8] |= a; + bitvec[pos / 8 + 1] = 0xFF; + bitvec[pos / 8 + 2] = (byte)~a; + } + + private const uint Vector128ByteCount = 16; + private const uint Vector128IntCount = 4; + private const uint Vector256ByteCount = 32; + private const uint Vector256IntCount = 8; + + public static bool CheckCollision(byte[] codeSegments, byte[] jumpmask) + { + int count = Math.Min(codeSegments.Length, jumpmask.Length); + + uint i = 0; + + ref byte left = ref MemoryMarshal.GetReference(codeSegments); + ref byte right = ref MemoryMarshal.GetReference(jumpmask); + + if (Vector256.IsHardwareAccelerated) + { + Vector256 zeros = Vector256.Create(0); + for (; i < (uint)count - (Vector256IntCount - 1u); i += Vector256IntCount) + { + Vector256 result = Vector256.LoadUnsafe(ref left, i) & Vector256.LoadUnsafe(ref right, i); + result = Vector256.Min(result, zeros); + if (Vector256.Sum(result) != 0) + { + return true; + } + } + } + else if (Vector128.IsHardwareAccelerated) + { + Vector128 zeros = Vector128.Create(0); + for (; i < (uint)count - (Vector128IntCount - 1u); i += Vector128IntCount) + { + Vector128 result = Vector128.LoadUnsafe(ref left, i) & Vector128.LoadUnsafe(ref right, i); + result = Vector128.Min(result, zeros); + if (Vector128.Sum(result) != 0) + { + return true; + } + } + } + + for (int j = (int)i; j < (uint)count; j++) + { + if ((codeSegments[j] & jumpmask[j]) != 0) + { + return true; + } + } + + return false; + } +} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs new file mode 100644 index 00000000000..98c1da0972f --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -0,0 +1,864 @@ +// SPDX-FileCopyrightText: 2023 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 Nethermind.Logging; + +[assembly: InternalsVisibleTo("Nethermind.EofParser")] + +namespace Nethermind.Evm.EOF; + +internal static class EvmObjectFormat +{ + [StructLayout(LayoutKind.Sequential)] + struct Worklet + { + public Worklet(ushort position, ushort stackHeight) + { + Position = position; + StackHeight = stackHeight; + } + public ushort Position; + public ushort StackHeight; + } + + private interface IEofVersionHandler + { + bool ValidateBody(ReadOnlySpan code, EofHeader header); + bool TryParseEofHeader(ReadOnlySpan code, [NotNullWhen(true)] out EofHeader? header); + } + + // magic prefix : EofFormatByte is the first byte, EofFormatDiff is chosen to diff from previously rejected contract according to EIP3541 + public static byte[] 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 lenght + + private static readonly Dictionary _eofVersionHandlers = new(); + internal static ILogger Logger { get; set; } = NullLogger.Instance; + + static EvmObjectFormat() + { + _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(ReadOnlySpan container) => container.StartsWith(MAGIC); + public static bool IsEofn(ReadOnlySpan container, byte version) => container.Length >= MAGIC.Length + 1 && container.StartsWith(MAGIC) && container[MAGIC.Length] == version; + + public static bool IsValidEof(ReadOnlySpan container, [NotNullWhen(true)] out EofHeader? header) + { + if (container.Length > VERSION_OFFSET + && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) + && handler.TryParseEofHeader(container, out header)) + { + EofHeader h = header.Value; + if (handler.ValidateBody(container, h)) + { + return true; + } + } + + header = null; + return false; + } + + public static bool TryExtractHeader(ReadOnlySpan container, [NotNullWhen(true)] out EofHeader? header) + { + header = null; + return container.Length > VERSION_OFFSET + && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) + && handler.TryParseEofHeader(container, out header); + } + + public static byte GetCodeVersion(ReadOnlySpan container) + { + return container.Length <= VERSION_OFFSET + ? byte.MinValue + : container[VERSION_OFFSET]; + } + + internal class Eof1 : IEofVersionHandler + { + private ref struct Sizes + { + public ushort TypeSectionSize; + public ushort CodeSectionSize; + public ushort DataSectionSize; + public ushort ContainerSectionSize; + } + + public const byte VERSION = 0x01; + internal enum Separator : byte + { + KIND_TYPE = 0x01, + KIND_CODE = 0x02, + KIND_DATA = 0x03, + KIND_CONTAINER = 0x04, + TERMINATOR = 0x00 + } + + internal const byte MINIMUM_HEADER_SECTION_SIZE = 3; + 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 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 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 MAX_STACK_HEIGHT_OFFSET = OUTPUTS_OFFSET + 1; + internal const int MAX_STACK_HEIGHT_LENGTH = 2; + internal const ushort MAX_STACK_HEIGHT = 0x3FF; + + internal const ushort MINIMUM_NUM_CODE_SECTIONS = 1; + internal const ushort MAXIMUM_NUM_CODE_SECTIONS = 1024; + internal const ushort MAXIMUM_NUM_CONTAINER_SECTIONS = 0x00FF; + internal const ushort RETURN_STACK_MAX_HEIGHT = MAXIMUM_NUM_CODE_SECTIONS; // the size in the type sectionn 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 + + MINIMUM_CONTAINERSECTION_SIZE; // minimum container section body size + + public bool TryParseEofHeader(ReadOnlySpan container, out EofHeader? header) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ushort GetUInt16(ReadOnlySpan container, int offset) => + container.Slice(offset, TWO_BYTE_LENGTH).ReadEthUInt16(); + + header = null; + // we need to be able to parse header + minimum section lenghts + if (container.Length < MINIMUM_SIZE) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + if (!container.StartsWith(MAGIC)) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Code doesn't start with Magic byte sequence expected {MAGIC.ToHexString(true)} "); + return false; + } + + if (container[VERSION_OFFSET] != VERSION) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Code is not Eof version {VERSION}"); + return false; + } + + Sizes sectionSizes = new(); + + int TYPESECTION_HEADER_STARTOFFSET = VERSION_OFFSET + ONE_BYTE_LENGTH; + int TYPESECTION_HEADER_ENDOFFSET = VERSION_OFFSET + ONE_BYTE_LENGTH + TWO_BYTE_LENGTH; + if (container[TYPESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_TYPE) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Code is not Eof version {VERSION}"); + return false; + } + sectionSizes.TypeSectionSize = GetUInt16(container, TYPESECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); + if (sectionSizes.TypeSectionSize < MINIMUM_TYPESECTION_SIZE) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); + return false; + } + + int CODESECTION_HEADER_STARTOFFSET = TYPESECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; + int CODESECTION_HEADER_ENDOFFSET = CODESECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; + if (container[CODESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_CODE) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Eof{VERSION}, Code header is not well formatted"); + return false; + } + + ushort numberOfCodeSections = GetUInt16(container, CODESECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); + sectionSizes.CodeSectionSize = (ushort)(numberOfCodeSections * TWO_BYTE_LENGTH); + if (numberOfCodeSections > MAXIMUM_NUM_CODE_SECTIONS) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : code sections count must not exceed 1024"); + return false; + } + + int[] codeSections = new int[numberOfCodeSections]; + int CODESECTION_HEADER_PREFIX_SIZE = CODESECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; + for (ushort pos = 0; pos < numberOfCodeSections; pos++) + { + int currentCodeSizeOffset = CODESECTION_HEADER_PREFIX_SIZE + pos * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size + int codeSectionSize = GetUInt16(container, currentCodeSizeOffset + ONE_BYTE_LENGTH); + + if (codeSectionSize == 0) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Empty Code Section are not allowed, CodeSectionSize must be > 0 but found {codeSectionSize}"); + return false; + } + + codeSections[pos] = codeSectionSize; + } + CODESECTION_HEADER_ENDOFFSET = CODESECTION_HEADER_PREFIX_SIZE + numberOfCodeSections * TWO_BYTE_LENGTH; + + int CONTAINERSECTION_HEADER_STARTOFFSET = CODESECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; + int? CONTAINERSECTION_HEADER_ENDOFFSET = null; + int[] containersSections = null; + if (container[CONTAINERSECTION_HEADER_STARTOFFSET] == (byte)Separator.KIND_CONTAINER) + { + int numberOfContainersSections = GetUInt16(container, CONTAINERSECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); + sectionSizes.ContainerSectionSize = (ushort)(numberOfContainersSections * TWO_BYTE_LENGTH); + + containersSections = new int[numberOfContainersSections]; + for (ushort pos = 0; pos < numberOfCodeSections; pos++) + { + int currentCodeSizeOffset = CODESECTION_HEADER_STARTOFFSET + pos * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size + int containerSectionSize = GetUInt16(container, currentCodeSizeOffset + ONE_BYTE_LENGTH); + + if (containerSectionSize == 0) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Empty Code Section are not allowed, CodeSectionSize must be > 0 but found {containerSectionSize}"); + return false; + } + + containersSections[pos] = containerSectionSize; + CONTAINERSECTION_HEADER_ENDOFFSET = currentCodeSizeOffset + containerSectionSize; + } + } + + + int DATASECTION_HEADER_STARTOFFSET = CONTAINERSECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH ?? CONTAINERSECTION_HEADER_STARTOFFSET; + int DATASECTION_HEADER_ENDOFFSET = DATASECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; + if (container[DATASECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_DATA) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Eof{VERSION}, Code header is not well formatted"); + return false; + } + sectionSizes.DataSectionSize = GetUInt16(container, DATASECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); + + + int HEADER_TERMINATOR_OFFSET = DATASECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; + if (container[HEADER_TERMINATOR_OFFSET] != (byte)Separator.TERMINATOR) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Eof{VERSION}, Code header is not well formatted"); + return false; + } + + SectionHeader typeSectionHeader = new + ( + Start: HEADER_TERMINATOR_OFFSET + ONE_BYTE_LENGTH, + Size: sectionSizes.TypeSectionSize + ); + + CompoundSectionHeader codeSectionHeader = new( + Start: typeSectionHeader.Start + typeSectionHeader.Size, + SubSectionsSizes: codeSections + ); + + CompoundSectionHeader? containerSectionHeader = containersSections is null ? null + : new( + Start: codeSectionHeader.Start + codeSectionHeader.Size, + SubSectionsSizes: containersSections + ); + + SectionHeader dataSectionHeader = new( + Start: containerSectionHeader?.Start + containerSectionHeader?.Size ?? codeSectionHeader.EndOffset, + Size: sectionSizes.DataSectionSize + ); + + header = new EofHeader + { + Version = VERSION, + TypeSection = typeSectionHeader, + CodeSections = codeSectionHeader, + ContainerSection = containerSectionHeader, + DataSection = dataSectionHeader, + }; + + return true; + } + + public bool ValidateBody(ReadOnlySpan container, EofHeader header) + { + int startOffset = header.TypeSection.Start; + int calculatedCodeLength = header.TypeSection.Size + + header.CodeSections.Size + + header.DataSection.Size + + (header.ContainerSection?.Size ?? 0); + CompoundSectionHeader codeSections = header.CodeSections; + ReadOnlySpan contractBody = container[startOffset..]; + (int typeSectionStart, ushort typeSectionSize) = header.TypeSection; + + if (header.ContainerSection?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) + { + // move this check where `header.ExtraContainers.Count` is parsed + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSection?.Count}"); + return false; + } + + if (contractBody.Length != calculatedCodeLength) + { + if (Logger.IsTrace) Logger.Trace("EIP-3540 : SectionSizes indicated in bundled header are incorrect, or ContainerCode is incomplete"); + return false; + } + + if (codeSections.Count == 0 || codeSections.SubSectionsSizes.Any(size => size == 0)) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : CodeSection size must follow a CodeSection, CodeSection length was {codeSections.Count}"); + return false; + } + + if (codeSections.Count != (typeSectionSize / MINIMUM_TYPESECTION_SIZE)) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4750: Code Sections count must match TypeSection count, CodeSection count was {codeSections.Count}, expected {typeSectionSize / MINIMUM_TYPESECTION_SIZE}"); + return false; + } + + ReadOnlySpan typesection = container.Slice(typeSectionStart, typeSectionSize); + if (!ValidateTypeSection(typesection)) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4750: invalid typesection found"); + return false; + } + + bool[] visitedSections = ArrayPool.Shared.Rent(header.CodeSections.Count); + Queue validationQueue = new Queue(); + validationQueue.Enqueue(0); + + + while (validationQueue.TryDequeue(out ushort sectionIdx)) + { + if (visitedSections[sectionIdx]) + { + continue; + } + + visitedSections[sectionIdx] = true; + (int codeSectionStartOffset, int codeSectionSize) = header.CodeSections[sectionIdx]; + + + bool isNonReturning = typesection[sectionIdx * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; + ReadOnlySpan code = container.Slice(codeSectionStartOffset, codeSectionSize); + if (!ValidateInstructions(sectionIdx, isNonReturning, typesection, code, header, validationQueue, out ushort jumpsCount)) + { + return false; + } + } + + return visitedSections[..header.CodeSections.Count].All(id => id); + } + + bool ValidateTypeSection(ReadOnlySpan types) + { + if (types[INPUTS_OFFSET] != 0 || types[OUTPUTS_OFFSET] != 0) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4750: first 2 bytes of type section must be 0s"); + return false; + } + + if (types.Length % MINIMUM_TYPESECTION_SIZE != 0) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4750: type section length must be a product of {MINIMUM_TYPESECTION_SIZE}"); + return false; + } + + for (int offset = 0; offset < types.Length; offset += MINIMUM_TYPESECTION_SIZE) + { + byte inputCount = types[offset + INPUTS_OFFSET]; + byte outputCount = types[offset + OUTPUTS_OFFSET]; + ushort maxStackHeight = types.Slice(offset + MAX_STACK_HEIGHT_OFFSET, MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16(); + + if (inputCount > INPUTS_MAX) + { + if (Logger.IsTrace) Logger.Trace("EIP-3540 : Too many inputs"); + return false; + } + + if (outputCount > OUTPUTS_MAX) + { + if (Logger.IsTrace) Logger.Trace("EIP-3540 : Too many outputs"); + return false; + } + + if (maxStackHeight > MAX_STACK_HEIGHT) + { + if (Logger.IsTrace) Logger.Trace("EIP-3540 : Stack depth too high"); + return false; + } + } + return true; + } + + bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, Queue worklist, out ushort jumpsCount) + { + byte[] codeBitmap = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); + byte[] jumpdests = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); + jumpsCount = 1; + try + { + int pos; + + for (pos = 0; pos < code.Length;) + { + Instruction opcode = (Instruction)code[pos]; + int postInstructionByte = pos + 1; + + if (!opcode.IsValid(IsEofContext: true)) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3670 : CodeSection contains undefined opcode {opcode}"); + return false; + } + + if (opcode is Instruction.RJUMP or Instruction.RJUMPI) + { + if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4200 : Static Relative Jump Argument underflow"); + return false; + } + + var offset = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthInt16(); + var rjumpdest = offset + TWO_BYTE_LENGTH + postInstructionByte; + + if (rjumpdest < 0 || rjumpdest >= code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4200 : Static Relative Jump Destination outside of Code bounds"); + return false; + } + + jumpsCount += opcode is Instruction.RJUMP ? ONE_BYTE_LENGTH : TWO_BYTE_LENGTH; + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpdests, ref rjumpdest); + BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + } + + if (opcode is Instruction.JUMPF) + { + if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-6206 : JUMPF Argument underflow"); + return false; + } + + var targetSectionId = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); + + BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + if (targetSectionId >= header.CodeSections.Count) + { + if (Logger.IsTrace) Logger.Trace($"EIP-6206 : JUMPF to unknown code section"); + return false; + } + + worklist.Enqueue(targetSectionId); + + + bool isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; + + if (isNonReturning && !isTargetSectionNonReturning) + { + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : JUMPF from non returning code-sections can only call non-returning sections"); + return false; + } + } + + if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) + { + if (postInstructionByte + ONE_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-663 : {opcode.FastToString()} Argument underflow"); + return false; + } + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + + } + + if (opcode is Instruction.RJUMPV) + { + if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4200 : Static Relative Jumpv Argument underflow"); + return false; + } + + byte count = code[postInstructionByte]; + jumpsCount += count; + if (count < MINIMUMS_ACCEPTABLE_JUMPV_JUMPTABLE_LENGTH) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4200 : jumpv jumptable must have at least 1 entry"); + return false; + } + + if (postInstructionByte + ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4200 : jumpv jumptable underflow"); + return false; + } + + var immediateValueSize = ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH; + for (int j = 0; j < count; j++) + { + var offset = code.Slice(postInstructionByte + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthInt16(); + var rjumpdest = offset + immediateValueSize + postInstructionByte; + if (rjumpdest < 0 || rjumpdest >= code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4200 : Static Relative Jumpv Destination outside of Code bounds"); + return false; + } + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpdests, ref rjumpdest); + } + BitmapHelper.HandleNumbits(immediateValueSize, codeBitmap, ref postInstructionByte); + } + + if (opcode is Instruction.CALLF) + { + if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4750 : CALLF Argument underflow"); + return false; + } + + ushort targetSectionId = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); + BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + + if (targetSectionId >= header.CodeSections.Count) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4750 : Invalid Section Id"); + return false; + } + + // begin block: might not be included in Eof2 + // byte targetSectionOutputCount = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + // if (targetSectionOutputCount == 0x80) + // { + // if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : CALLF into non-returning function"); + // return false; + // } + // end block + + worklist.Enqueue(targetSectionId); + } + + if (opcode is Instruction.RETF && typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80) + { + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : non returning sections are not allowed to use opcode {Instruction.RETF}"); + return false; + } + + if (opcode is Instruction.DATALOADN) + { + if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : DATALOADN Argument underflow"); + return false; + } + + ushort dataSectionOffset = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); + BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + + if (dataSectionOffset * 32 >= header.DataSection.Size) + { + + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : DATALOADN's immediate argument must be less than datasection.Length / 32 i.e : {header.DataSection.Size / 32}"); + return false; + } + } + + if (opcode is Instruction.CREATE3) + { + if (postInstructionByte + ONE_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : CREATE3 Argument underflow"); + return false; + } + + ushort initcodeSectionId = code[postInstructionByte + ONE_BYTE_LENGTH]; + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + + if (initcodeSectionId >= header.ContainerSection?.Count) + { + + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : CREATE3's immediate must falls within the Containers' range available, i.e : {header.CodeSections.Count}"); + return false; + } + } + + if (opcode is >= Instruction.PUSH0 and <= Instruction.PUSH32) + { + int len = opcode - Instruction.PUSH0; + if (postInstructionByte + len > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3670 : PC Reached out of bounds"); + return false; + } + BitmapHelper.HandleNumbits(len, codeBitmap, ref postInstructionByte); + } + pos = postInstructionByte; + } + + if (pos > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3670 : PC Reached out of bounds"); + return false; + } + + bool result = !BitmapHelper.CheckCollision(codeBitmap, jumpdests); + if (!result) + { + if (Logger.IsTrace) Logger.Trace($"EIP-4200 : Invalid Jump destination"); + } + + if (!ValidateStackState(sectionId, code, typesection, jumpsCount)) + { + return false; + } + return result; + } + finally + { + ArrayPool.Shared.Return(codeBitmap, true); + ArrayPool.Shared.Return(jumpdests, true); + } + } + public bool ValidateReachableCode(in ReadOnlySpan code, short[] reachedOpcode) + { + for (int pos = 0; pos < code.Length;) + { + var opcode = (Instruction)code[pos]; + + if (reachedOpcode[pos] == 0) + { + return false; + } + + pos++; + if (opcode is Instruction.RJUMP or Instruction.RJUMPI or Instruction.CALLF or Instruction.JUMPF) + { + pos += TWO_BYTE_LENGTH; + } + else if (opcode is Instruction.RJUMPV) + { + byte count = code[pos]; + + pos += ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH; + } + else if (opcode is Instruction.SWAPN or Instruction.DUPN or Instruction.EXCHANGE) + { + pos += ONE_BYTE_LENGTH; + } + else if (opcode is >= Instruction.PUSH1 and <= Instruction.PUSH32) + { + int len = opcode - Instruction.PUSH0; + pos += len; + } + else if (opcode is Instruction.CREATE3) + { + pos += ONE_BYTE_LENGTH; + } + else if (opcode is Instruction.DATALOADN) + { + pos += TWO_BYTE_LENGTH; + } + } + return true; + } + public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection, ushort worksetCount) + { + static Worklet PopWorklet(Worklet[] workset, ref ushort worksetPointer) => workset[worksetPointer++]; + static void PushWorklet(Worklet[] workset, ref ushort worksetTop, Worklet worklet) => workset[worksetTop++] = worklet; + + short[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); + ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); + int curr_outputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + int peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + + ushort worksetTop = 0; ushort worksetPointer = 0; + Worklet[] workset = ArrayPool.Shared.Rent(worksetCount + 1); + + try + { + PushWorklet(workset, ref worksetTop, new Worklet(0, (ushort)peakStackHeight)); + while (worksetPointer < worksetTop) + { + Worklet worklet = PopWorklet(workset, ref worksetPointer); + bool stop = false; + + while (!stop) + { + Instruction opcode = (Instruction)code[worklet.Position]; + (ushort? inputs, ushort? outputs, ushort? immediates) = opcode.StackRequirements(); + ushort posPostInstruction = (ushort)(worklet.Position + 1); + if (recordedStackHeight[worklet.Position] != 0) + { + if (worklet.StackHeight != recordedStackHeight[worklet.Position] - 1) + { + if (Logger.IsTrace) Logger.Trace($"EIP-5450 : Branch joint line has invalid stack height"); + return false; + } + break; + } + else + { + recordedStackHeight[worklet.Position] = (short)(worklet.StackHeight + 1); + } + + switch (opcode) + { + case Instruction.CALLF or Instruction.JUMPF: + ushort sectionIndex = code.Slice(posPostInstruction, TWO_BYTE_LENGTH).ReadEthUInt16(); + inputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + + outputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + outputs = (ushort)(outputs == 0x80 ? 0 : outputs); + + ushort maxStackHeigh = typesection.Slice(sectionIndex * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); + + if (worklet.StackHeight + maxStackHeigh > MAX_STACK_HEIGHT) + { + if (Logger.IsTrace) Logger.Trace($"EIP-5450 : stack head during callf must not exceed {MAX_STACK_HEIGHT}"); + return false; + } + break; + case Instruction.DUPN: + byte imm = code[posPostInstruction]; + inputs = (ushort)(imm + 1); + outputs = (ushort)(inputs + 1); + break; + case Instruction.SWAPN: + imm = code[posPostInstruction]; + outputs = inputs = (ushort)(1 + imm); + break; + case Instruction.EXCHANGE: + byte imm_n = (byte)((code[posPostInstruction] >> 4) + 1); + byte imm_m = (byte)((code[posPostInstruction] & 0x0F) + 1); + outputs = inputs = Math.Max(imm_n, imm_m); + break; + } + + if (worklet.StackHeight < inputs) + { + if (Logger.IsTrace) Logger.Trace($"EIP-5450 : Stack Underflow required {inputs} but found {worklet.StackHeight}"); + return false; + } + + worklet.StackHeight += (ushort)(outputs - inputs + (opcode is Instruction.JUMPF ? curr_outputs : 0)); + peakStackHeight = Math.Max(peakStackHeight, worklet.StackHeight); + + switch (opcode) + { + case Instruction.JUMPF: + { + if (curr_outputs < outputs) + { + if (Logger.IsTrace) Logger.Trace($"EIP-6206 : Output Count {outputs} must be less or equal than sectionId {sectionId} output count {curr_outputs}"); + return false; + } + + if (worklet.StackHeight != curr_outputs + inputs - outputs) + { + if (Logger.IsTrace) Logger.Trace($"EIP-6206 : Stack Height must {curr_outputs + inputs - outputs} but found {worklet.StackHeight}"); + return false; + } + + break; + } + case Instruction.RJUMP: + { + short offset = code.Slice(posPostInstruction, TWO_BYTE_LENGTH).ReadEthInt16(); + int jumpDestination = posPostInstruction + immediates.Value + offset; + PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); + stop = true; + break; + } + case Instruction.RJUMPI: + { + var offset = code.Slice(posPostInstruction, TWO_BYTE_LENGTH).ReadEthInt16(); + var jumpDestination = posPostInstruction + immediates + offset; + PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); + posPostInstruction += immediates.Value; + break; + } + case Instruction.RJUMPV: + { + var count = code[posPostInstruction]; + immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); + for (short j = 0; j < count; j++) + { + int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; + int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); + int jumpDestination = posPostInstruction + immediates.Value + offset; + PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); + } + posPostInstruction += immediates.Value; + break; + } + default: + { + posPostInstruction += immediates.Value; + break; + } + } + + worklet.Position = posPostInstruction; + if (stop) break; + + if (opcode.IsTerminating()) + { + var expectedHeight = opcode is Instruction.RETF ? typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] : worklet.StackHeight; + if (expectedHeight != worklet.StackHeight) + { + if (Logger.IsTrace) Logger.Trace($"EIP-5450 : Stack state invalid required height {expectedHeight} but found {worklet.StackHeight}"); + return false; + } + break; + } + + else if (worklet.Position >= code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-5450 : Invalid code, reached end of code without a terminating instruction"); + return false; + } + } + } + + if (!ValidateReachableCode(code, recordedStackHeight)) + { + if (Logger.IsTrace) Logger.Trace($"EIP-5450 : bytecode has unreachable segments"); + return false; + } + + if (peakStackHeight != suggestedMaxHeight) + { + if (Logger.IsTrace) Logger.Trace($"EIP-5450 : Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); + return false; + } + + bool result = peakStackHeight <= MAX_STACK_HEIGHT; + if (!result) + { + if (Logger.IsTrace) Logger.Trace($"EIP-5450 : stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); + return false; + } + return result; + } + finally + { + ArrayPool.Shared.Return(recordedStackHeight, true); + ArrayPool.Shared.Return(workset, true); + } + } + } +} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs new file mode 100644 index 00000000000..0a611c6fa0d --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Drawing; +using System.Linq; +using Nethermind.Core.Extensions; +using Nethermind.Evm.CodeAnalysis; + +namespace Nethermind.Evm.EOF; + +public struct EofHeader +{ + public required byte Version; + public required SectionHeader TypeSection; + public required CompoundSectionHeader CodeSections; + public required SectionHeader DataSection; + public required CompoundSectionHeader? ContainerSection; + + public EofHeader() + { + } +} + +public readonly record struct SectionHeader(int Start, ushort Size) +{ + public int EndOffset => Start + Size; +} + +public readonly record struct CompoundSectionHeader(int Start, int[] SubSectionsSizes) +{ + public int EndOffset => Start + SubSectionsSizes.Sum(); + public int Size => EndOffset - Start; + public int Count => SubSectionsSizes.Length; + + public SectionHeader this[int i] => new SectionHeader(Start: Start + SubSectionsSizes[..i].Sum(), Size: (ushort)SubSectionsSizes[i]); +} From 94f5672a25ccea7ce59f9b74d7bd8257a0965ea2 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jan 2024 23:44:49 +0100 Subject: [PATCH 005/255] added legacy call* opcode changes --- .../EvmObjectFormat/EofCodeValidator.cs | 15 +++++++- src/Nethermind/Nethermind.Evm/Instruction.cs | 24 +++++++----- .../Nethermind.Evm/VirtualMachine.cs | 37 ++++++++++++++++--- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 98c1da0972f..03224f6d9a4 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -55,7 +55,20 @@ static EvmObjectFormat() /// /// Machine code to be checked /// - public static bool IsEof(ReadOnlySpan container) => container.StartsWith(MAGIC); + public static bool IsEof(ReadOnlySpan container, [NotNullWhen(true)] out int version) + { + if(container.Length >= MAGIC.Length + 1) + { + version = container[MAGIC.Length]; + return container.StartsWith(MAGIC); + } else + { + version = 0; + return false; + } + + } + public static bool IsEofn(ReadOnlySpan container, byte version) => container.Length >= MAGIC.Length + 1 && container.StartsWith(MAGIC) && container[MAGIC.Length] == version; public static bool IsValidEof(ReadOnlySpan container, [NotNullWhen(true)] out EofHeader? header) diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 8a844f6e7e0..89bc65c68b1 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -227,17 +227,23 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) return instruction switch { - Instruction.CREATE2 or Instruction.CREATE => !IsEofContext, - Instruction.EXTCODEHASH or Instruction.EXTCODECOPY or Instruction.EXTCODESIZE => !IsEofContext, - Instruction.CODECOPY or Instruction.CODESIZE => !IsEofContext, - Instruction.GAS => !IsEofContext, - Instruction.PC => !IsEofContext, - Instruction.CALLCODE or Instruction.SELFDESTRUCT => !IsEofContext, - Instruction.JUMPI or Instruction.JUMP => !IsEofContext, Instruction.CALLF or Instruction.RETF or Instruction.JUMPF => IsEofContext, Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE => IsEofContext, - Instruction.BEGINSUB or Instruction.RETURNSUB or Instruction.JUMPSUB => true, - Instruction.CALL or Instruction.DELEGATECALL or Instruction.GAS => !IsEofContext, + Instruction.RJUMP or Instruction.RJUMPI or Instruction.RJUMPV => IsEofContext, + Instruction.CALL => !IsEofContext, + Instruction.CALLCODE => !IsEofContext, + Instruction.DELEGATECALL => !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 }; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d388f58a775..1352682fdb0 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -32,7 +32,10 @@ namespace Nethermind.Evm; using System.Linq; +using System.Reflection.PortableExecutable; +using System.Security.Cryptography; using Int256; +using Nethermind.Evm.EOF; public class VirtualMachine : IVirtualMachine { @@ -1398,11 +1401,20 @@ private CallResult ExecuteCode account = _state.GetCode(address); + if(spec.IsEofEnabled && EvmObjectFormat.IsEof(account, out _)) + { + stack.PushBytes(SHA256.HashData(EvmObjectFormat.MAGIC)); + } + else + { + stack.PushBytes(_state.GetCodeHash(address).Bytes); + } } break; @@ -2171,8 +2191,15 @@ private CallResult ExecuteCode(Address address, ref EvmStack stack, IReleaseSpec spec) where TTracingInstructions : struct, IIsTracing { byte[] accountCode = GetCachedCodeInfo(_worldState, address, spec).MachineCode; - UInt256 result = (UInt256)accountCode.Length; - stack.PushUInt256(in result); + if (spec.IsEofEnabled && EvmObjectFormat.IsEof(accountCode, out _)) + { + stack.PushUInt256(2); + } + else + { + UInt256 result = (UInt256)accountCode.Length; + stack.PushUInt256(in result); + } } [SkipLocalsInit] From f93ed8c4dc33dc4279ed9d9e87e1116be2890a57 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 18 Jan 2024 09:44:36 +0100 Subject: [PATCH 006/255] added EofInitcode tx validation --- .../Validators/TxValidator.cs | 16 +++++++++++++++- .../Builders/TransactionBuilder.cs | 6 ++++++ src/Nethermind/Nethermind.Core/Transaction.cs | 1 + .../TransactionProcessor.cs | 2 +- .../Nethermind.Evm/TxExecutionContext.cs | 4 +++- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index 65c44357101..f1b42c056be 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -40,7 +40,8 @@ public bool IsWellFormed(Transaction transaction, IReleaseSpec releaseSpec) ValidateChainId(transaction) && Validate1559GasFields(transaction, releaseSpec) && Validate3860Rules(transaction, releaseSpec) && - Validate4844Fields(transaction); + Validate4844Fields(transaction) && + ValidateMegaEofInitcodes(transaction, releaseSpec); } private static bool Validate3860Rules(Transaction transaction, IReleaseSpec releaseSpec) => @@ -106,6 +107,19 @@ private bool ValidateSignature(Transaction tx, IReleaseSpec spec) return !spec.ValidateChainId; } + private static bool ValidateMegaEofInitcodes(Transaction transaction, IReleaseSpec spec) + { + if (!spec.IsEofEnabled) return true; + + if (transaction.Initcodes?.Length >= Transaction.MaxInitcodeCount) return false; + + foreach (var initcode in transaction.Initcodes) + { + if (initcode?.Length >= spec.MaxInitCodeSize) return false; + } + return true; + } + private static bool Validate4844Fields(Transaction transaction) { // Execution-payload version verification diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs index a06da3f81ef..e44bc29b48f 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs @@ -27,6 +27,12 @@ public TransactionBuilder() }; } + public TransactionBuilder WithEofInitcodes(byte[][] initcodes) + { + TestObjectInternal.Initcodes = initcodes; + return this; + } + public TransactionBuilder WithNonce(UInt256 nonce) { TestObjectInternal.Nonce = nonce; diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index 6000a75fc05..b9ee159c74a 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -20,6 +20,7 @@ namespace Nethermind.Core public class Transaction { public const int BaseTxGasCost = 21000; + public const int MaxInitcodeCount = 256; public ulong? ChainId { get; set; } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 90d039ff7eb..e7db44433d1 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -441,7 +441,7 @@ protected virtual ExecutionEnvironment BuildExecutionEnvironment( throw new InvalidDataException("Recipient has not been resolved properly before tx execution"); TxExecutionContext executionContext = - new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes); + new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, tx.Initcodes); CodeInfo codeInfo = tx.IsContractCreation ? new(tx.Data.AsArray()) : VirtualMachine.GetCachedCodeInfo(WorldState, recipient, spec); diff --git a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs index 637c6fc0258..7223b7a23c8 100644 --- a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs +++ b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs @@ -12,13 +12,15 @@ public readonly struct TxExecutionContext public Address Origin { get; } public UInt256 GasPrice { get; } public byte[][]? BlobVersionedHashes { get; } + public byte[][]? Initicodes { get; } - public TxExecutionContext(in BlockExecutionContext blockExecutionContext, Address origin, in UInt256 gasPrice, byte[][] blobVersionedHashes) + public TxExecutionContext(in BlockExecutionContext blockExecutionContext, Address origin, in UInt256 gasPrice, byte[][] blobVersionedHashes, byte[][] initicodes) { BlockExecutionContext = blockExecutionContext; Origin = origin; GasPrice = gasPrice; BlobVersionedHashes = blobVersionedHashes; + Initicodes = initicodes; } } } From 8a45800ae1ff739b521bc0f155573ac538369c47 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 18 Jan 2024 10:03:52 +0100 Subject: [PATCH 007/255] added static relative jumps --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 6 ++- src/Nethermind/Nethermind.Evm/GasCostOf.cs | 20 +++++++++ src/Nethermind/Nethermind.Evm/Instruction.cs | 12 ++++- .../Nethermind.Evm/VirtualMachine.cs | 45 +++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 73d517ad78d..1460e9f5f26 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -3,7 +3,7 @@ using System; using System.Runtime.CompilerServices; - +using Nethermind.Evm.EOF; using Nethermind.Evm.Precompiles; namespace Nethermind.Evm.CodeAnalysis @@ -11,12 +11,16 @@ namespace Nethermind.Evm.CodeAnalysis public class CodeInfo { public byte[] MachineCode { get; set; } + + public bool IsEof; + public int Version; public IPrecompile? Precompile { get; set; } private JumpDestinationAnalyzer? _analyzer; public CodeInfo(byte[] code) { MachineCode = code; + IsEof = EvmObjectFormat.IsEof(code, out this.Version); } public bool IsPrecompile => Precompile is not null; diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index 68dc8cae654..cd6aee9b8c0 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -62,5 +62,25 @@ public static class GasCostOf public const long AccessStorageListEntry = 1900; // eip-2930 public const long TLoad = WarmStateRead; // eip-1153 public const long TStore = WarmStateRead; // eip-1153 + + public const long DataLoad = 4; + public const long DataLoadN = 3; + public const long DataCopy = 3; + public const long DataSize = 2; + public const long ReturnContract = 0; + public const long Create3 = 32000; + public const long Create4 = 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 = 4; } } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 89bc65c68b1..5c8822b39e7 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -194,8 +194,13 @@ public enum Instruction : byte DUPN = 0xe6, SWAPN = 0xe7, - EXCHANGE = 0xf8, - RETURNDATALOAD = 0xf7 + EXCHANGE = 0xf8, // random value opcode spec has collision + RETURNDATALOAD = 0xf7, + + // opcode value not spec-ed + EOFCALL = 0xba, + EOFSTATICCALL = 0xbb, // StaticCallEnabled + EOFDELEGATECALL = 0xbc, // DelegateCallEnabled } public static class InstructionExtensions @@ -355,6 +360,9 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) spec ??= Frontier.Instance; return instruction switch { + Instruction.EOFCALL => "CALL" , + Instruction.EOFSTATICCALL => "STATICCALL", // StaticCallEnabled + Instruction.EOFDELEGATECALL => "DELEGATECALL", Instruction.PREVRANDAO when !isPostMerge => "DIFFICULTY", Instruction.RJUMP => spec.IsEofEnabled ? "RJUMP" : "BEGINSUB", Instruction.RJUMPI => spec.IsEofEnabled ? "RJUMPI" : "RETURNSUB", diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 1352682fdb0..1a20a3a520a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2113,6 +2113,51 @@ private CallResult ExecuteCode condition = stack.PopWord256(); + short offset = code.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); + if (!condition.SequenceEqual(BytesZero32)) + { + programCounter += offset; + } + programCounter += EvmObjectFormat.TWO_BYTE_LENGTH; + } + goto InvalidInstruction; + } + case Instruction.RJUMPV: + { + if (spec.IsEofEnabled && env.CodeInfo.IsEof) + { + if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; + var caseV = stack.PopByte(); + var maxIndex = code[programCounter++]; + var immediateValueSize = (maxIndex + 1) * EvmObjectFormat.TWO_BYTE_LENGTH; + if (caseV <= maxIndex) + { + int caseOffset = code.Slice( + programCounter + caseV * EvmObjectFormat.TWO_BYTE_LENGTH, + EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); + programCounter += caseOffset; + } + programCounter += immediateValueSize; + } + goto InvalidInstruction; + } default: { goto InvalidInstruction; From 420505d0661329adab9e7de44ab0ed701ddf3cee Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 18 Jan 2024 10:13:45 +0100 Subject: [PATCH 008/255] added prototype implementation for : dupn, swapn, exchange --- .../EvmObjectFormat/EofCodeValidator.cs | 2 +- src/Nethermind/Nethermind.Evm/EvmStack.cs | 21 +++++++++ .../Nethermind.Evm/VirtualMachine.cs | 44 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 03224f6d9a4..3285e01a783 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -757,7 +757,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea case Instruction.EXCHANGE: byte imm_n = (byte)((code[posPostInstruction] >> 4) + 1); byte imm_m = (byte)((code[posPostInstruction] & 0x0F) + 1); - outputs = inputs = Math.Max(imm_n, imm_m); + outputs = inputs = (ushort)(imm_n + imm_m); break; } diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index b59af410489..5b8538516de 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -408,6 +408,27 @@ public readonly bool Swap(int depth) return true; } + public readonly bool Exchange(int n, int m) + { + if (!EnsureDepth(n + m)) return false; + + ref byte bytes = ref MemoryMarshal.GetReference(_bytes); + + ref byte bottom = ref Unsafe.Add(ref bytes, (n - m) * WordSize); + ref byte top = ref Unsafe.Add(ref bytes, (n - 1) * WordSize); + + Word buffer = Unsafe.ReadUnaligned(ref bottom); + Unsafe.WriteUnaligned(ref bottom, Unsafe.ReadUnaligned(ref top)); + Unsafe.WriteUnaligned(ref top, buffer); + + if (typeof(TTracing) == typeof(IsTracing)) + { + Trace(n + m); + } + + return true; + } + private readonly void Trace(int depth) { for (int i = depth; i > 0; i--) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 1a20a3a520a..50cc82da1a3 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1804,6 +1804,20 @@ private CallResult ExecuteCode> 0x04 + 1; + int m = (int)code[programCounter] & 0x0f + 1; + + stack.Exchange(n, m); + + programCounter += 1; + break; + } case Instruction.LOG0: case Instruction.LOG1: case Instruction.LOG2: From 8d8c8ea069a23b99789d669edc61d5f3939ff2be Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 18 Jan 2024 17:41:08 +0100 Subject: [PATCH 009/255] added function section codes --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 5 +- .../Nethermind.Evm/CodeInfoFactory.cs | 18 +++ .../EvmObjectFormat/EofCodeInfo.cs | 55 ++++++++ .../EvmObjectFormat/EofHeader.cs | 11 +- src/Nethermind/Nethermind.Evm/EvmStack.cs | 2 +- src/Nethermind/Nethermind.Evm/EvmState.cs | 22 +++- .../Nethermind.Evm/ExecutionEnvironment.cs | 2 +- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 24 ++++ .../Nethermind.Evm/VirtualMachine.cs | 124 +++++++++++++----- 9 files changed, 212 insertions(+), 51 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs create mode 100644 src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs create mode 100644 src/Nethermind/Nethermind.Evm/ICodeInfo.cs diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 1460e9f5f26..bc56d3eb2d5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -8,19 +8,16 @@ namespace Nethermind.Evm.CodeAnalysis { - public class CodeInfo + public class CodeInfo : ICodeInfo { public byte[] MachineCode { get; set; } - public bool IsEof; - public int Version; public IPrecompile? Precompile { get; set; } private JumpDestinationAnalyzer? _analyzer; public CodeInfo(byte[] code) { MachineCode = code; - IsEof = EvmObjectFormat.IsEof(code, out this.Version); } public bool IsPrecompile => Precompile is not null; diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs new file mode 100644 index 00000000000..d26eee71a9b --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Specs; +using Nethermind.Evm.EOF; + +namespace Nethermind.Evm.CodeAnalysis; + +public static class CodeInfoFactory +{ + public static ICodeInfo CreateCodeInfo(byte[] code, IReleaseSpec spec) + { + CodeInfo codeInfo = new(code); + return spec.IsEofEnabled && EvmObjectFormat.IsValidEof(code, out EofHeader? header) + ? new EofCodeInfo(codeInfo, header.Value) + : codeInfo; + } +} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs new file mode 100644 index 00000000000..9f638fa0ff6 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.Extensions; +using Nethermind.Evm.EOF; +using Nethermind.Evm.Precompiles; + +namespace Nethermind.Evm.CodeAnalysis; + +public class EofCodeInfo : ICodeInfo +{ + private readonly CodeInfo _codeInfo; + private readonly EofHeader _header; + public byte[] MachineCode => _codeInfo.MachineCode; + public IPrecompile? Precompile => _codeInfo.Precompile; + public byte Version => _header.Version; + public ReadOnlyMemory TypeSection { get; } + public ReadOnlyMemory CodeSection { get; } + public ReadOnlyMemory DataSection { get; } + public ReadOnlyMemory? ContainerSection { get; } + public SectionHeader SectionOffset(int sectionId) => _header.CodeSections[sectionId]; + public SectionHeader ContainerOffset(int containerId) => + _header.ContainerSection is null + ? throw new System.Diagnostics.UnreachableException() + : _header.ContainerSection.Value[containerId]; + public (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) + { + ReadOnlySpan typesectionSpan = TypeSection.Span; + int TypeSectionSectionOffset = index * EvmObjectFormat.Eof1.MINIMUM_TYPESECTION_SIZE; + return + ( + typesectionSpan[TypeSectionSectionOffset + EvmObjectFormat.Eof1.INPUTS_OFFSET], + typesectionSpan[TypeSectionSectionOffset + EvmObjectFormat.Eof1.OUTPUTS_OFFSET], + typesectionSpan.Slice(TypeSectionSectionOffset + EvmObjectFormat.Eof1.MAX_STACK_HEIGHT_OFFSET, EvmObjectFormat.Eof1.MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16() + ); + } + + public bool ValidateJump(int destination, bool isSubroutine) + { + throw new NotImplementedException(); + } + + public EofCodeInfo(CodeInfo codeInfo, in EofHeader header) + { + _codeInfo = codeInfo; + _header = header; + ReadOnlyMemory memory = MachineCode.AsMemory(); + TypeSection = memory.Slice(_header.TypeSection.Start, _header.TypeSection.Size); + CodeSection = memory.Slice(_header.CodeSections[0].Start, _header.CodeSections.Size); + DataSection = memory.Slice(_header.DataSection.Start, _header.DataSection.Size); + ContainerSection = _header.ContainerSection is null ? null + : memory.Slice(_header.ContainerSection.Value[0].Start, _header.ContainerSection.Value.Size); + } +} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index 0a611c6fa0d..617d834a6d7 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -8,17 +8,13 @@ namespace Nethermind.Evm.EOF; -public struct EofHeader +public struct EofHeader() { public required byte Version; public required SectionHeader TypeSection; public required CompoundSectionHeader CodeSections; public required SectionHeader DataSection; public required CompoundSectionHeader? ContainerSection; - - public EofHeader() - { - } } public readonly record struct SectionHeader(int Start, ushort Size) @@ -28,9 +24,10 @@ public readonly record struct SectionHeader(int Start, ushort Size) public readonly record struct CompoundSectionHeader(int Start, int[] SubSectionsSizes) { - public int EndOffset => Start + SubSectionsSizes.Sum(); + public readonly int EndOffset = Start + SubSectionsSizes.Sum(); public int Size => EndOffset - Start; public int Count => SubSectionsSizes.Length; - public SectionHeader this[int i] => new SectionHeader(Start: Start + SubSectionsSizes[..i].Sum(), Size: (ushort)SubSectionsSizes[i]); + // returns a subsection with localized indexing [0, size] ==> 0 is local to the section not the entire bytecode + public SectionHeader this[int i] => new SectionHeader(Start: SubSectionsSizes[..i].Sum(), Size: (ushort)SubSectionsSizes[i]); } diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 5b8538516de..b04d34f5171 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -333,7 +333,7 @@ public byte PopByte() return _bytes[Head * WordSize + WordSize - sizeof(byte)]; } - public void PushLeftPaddedBytes(Span value, int paddingLength) + public void PushLeftPaddedBytes(ReadOnlySpan value, int paddingLength) { if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value); diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index eeb788e49ae..c0caa72e920 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -20,6 +20,13 @@ namespace Nethermind.Evm [DebuggerDisplay("{ExecutionType} to {Env.ExecutingAccount}, G {GasAvailable} R {Refund} PC {ProgramCounter} OUT {OutputDestination}:{OutputLength}")] public class EvmState : IDisposable // TODO: rename to CallState { + public struct ReturnState + { + public int Index; + public int Offset; + public int Height; + } + private class StackPool { private readonly int _maxCallStackDepth; @@ -31,7 +38,7 @@ public StackPool(int maxCallStackDepth = VirtualMachine.MaxCallDepth * 2) } private readonly ConcurrentStack _dataStackPool = new(); - private readonly ConcurrentStack _returnStackPool = new(); + private readonly ConcurrentStack _returnStackPool = new(); private int _dataStackPoolDepth; private int _returnStackPoolDepth; @@ -42,7 +49,7 @@ public StackPool(int maxCallStackDepth = VirtualMachine.MaxCallDepth * 2) /// /// /// - public void ReturnStacks(byte[] dataStack, int[] returnStack) + public void ReturnStacks(byte[] dataStack, ReturnState[] returnStack) { _dataStackPool.Push(dataStack); _returnStackPool.Push(returnStack); @@ -64,9 +71,9 @@ private byte[] RentDataStack() return new byte[(EvmStack.MaxStackSize + EvmStack.RegisterLength) * 32]; } - private int[] RentReturnStack() + private ReturnState[] RentReturnStack() { - if (_returnStackPool.TryPop(out int[] result)) + if (_returnStackPool.TryPop(out ReturnState[] result)) { return result; } @@ -77,10 +84,10 @@ private int[] RentReturnStack() EvmStack.ThrowEvmStackOverflowException(); } - return new int[EvmStack.ReturnStackSize]; + return new ReturnState[EvmStack.ReturnStackSize]; } - public (byte[], int[]) RentStacks() + public (byte[], ReturnState[]) RentStacks() { return (RentDataStack(), RentReturnStack()); } @@ -89,7 +96,7 @@ private int[] RentReturnStack() public byte[]? DataStack; - public int[]? ReturnStack; + public ReturnState[]? ReturnStack; /// /// EIP-2929 accessed addresses @@ -228,6 +235,7 @@ public Address From public long GasAvailable { get; set; } public int ProgramCounter { get; set; } + public int FunctionIndex { get; set; } public long Refund { get; set; } public Address To => Env.CodeSource ?? Env.ExecutingAccount; diff --git a/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs b/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs index ec6600fef8e..a62f3198a55 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs @@ -36,7 +36,7 @@ public ExecutionEnvironment /// /// Parsed bytecode for the current call. /// - public readonly CodeInfo CodeInfo; + public readonly ICodeInfo CodeInfo; /// /// Currently executing account (in DELEGATECALL this will be equal to caller). diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs new file mode 100644 index 00000000000..e77f64ddbb1 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Diagnostics; +using Nethermind.Evm.Precompiles; + +namespace Nethermind.Evm.CodeAnalysis; + +public interface ICodeInfo +{ + int Version => 0; + byte[] MachineCode { get; } + IPrecompile? Precompile { get; } + bool IsPrecompile => Precompile is not null; + ReadOnlyMemory TypeSection => Memory.Empty; + ReadOnlyMemory CodeSection => MachineCode; + ReadOnlyMemory DataSection => Memory.Empty; + ReadOnlyMemory ContainerSection => Memory.Empty; + (int start, int size) SectionOffset(int idx) => idx == 0 ? (0, MachineCode.Length) : throw new ArgumentOutOfRangeException(); + (int start, int size) ContainerOffset(int idx) => idx == 0 ? (0, 0) : throw new ArgumentOutOfRangeException(); + (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); + bool ValidateJump(int destination, bool isSubroutine); +} diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 50cc82da1a3..103271e29a8 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -773,11 +773,19 @@ private CallResult ExecuteCode code = env.CodeInfo.MachineCode.AsSpan(); + + int programCounter = vmState.ProgramCounter; + int sectionIndex = vmState.FunctionIndex; + + ReadOnlySpan codeSection = env.CodeInfo.CodeSection.Span; + ReadOnlySpan dataSection = env.CodeInfo.DataSection.Span; + ReadOnlySpan typeSection = env.CodeInfo.TypeSection.Span; + ReadOnlySpan containerSection = env.CodeInfo.ContainerSection.Span; + EvmExceptionType exceptionType = EvmExceptionType.None; bool isRevert = false; #if DEBUG @@ -790,13 +798,13 @@ private CallResult ExecuteCode= code.Length) + if (programCounterInt >= codeSection.Length) { stack.PushZero(); } else { - stack.PushByte(code[programCounterInt]); + stack.PushByte(codeSection[programCounterInt]); } programCounter++; @@ -1776,8 +1784,8 @@ private CallResult ExecuteCode 0)) goto InvalidInstruction; if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) goto OutOfGas; - byte imm = code[programCounter]; + byte imm = codeSection[programCounter]; stack.Dup(imm + 1); programCounter += 1; @@ -1842,13 +1850,13 @@ private CallResult ExecuteCode 0)) goto InvalidInstruction; if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) goto OutOfGas; - byte imm = code[programCounter]; + byte imm = codeSection[programCounter]; stack.Swap(imm + 1); programCounter += 1; @@ -1856,14 +1864,14 @@ private CallResult ExecuteCode 0)) goto InvalidInstruction; if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) goto OutOfGas; - int n = (int)code[programCounter] >> 0x04 + 1; - int m = (int)code[programCounter] & 0x0f + 1; + int n = (int)codeSection[programCounter] >> 0x04 + 1; + int m = (int)codeSection[programCounter] & 0x0f + 1; stack.Exchange(n, m); @@ -2115,7 +2123,7 @@ private CallResult ExecuteCode 0)) { if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) goto OutOfGas; - short offset = code.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); + short offset = codeSection.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); programCounter += EvmObjectFormat.TWO_BYTE_LENGTH + offset; break; } @@ -2170,11 +2181,11 @@ private CallResult ExecuteCode 0)) { if (!UpdateGas(GasCostOf.RJumpi, ref gasAvailable)) goto OutOfGas; Span condition = stack.PopWord256(); - short offset = code.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); + short offset = codeSection.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); if (!condition.SequenceEqual(BytesZero32)) { programCounter += offset; @@ -2185,15 +2196,15 @@ private CallResult ExecuteCode 0)) { if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; var caseV = stack.PopByte(); - var maxIndex = code[programCounter++]; + var maxIndex = codeSection[programCounter++]; var immediateValueSize = (maxIndex + 1) * EvmObjectFormat.TWO_BYTE_LENGTH; if (caseV <= maxIndex) { - int caseOffset = code.Slice( + int caseOffset = codeSection.Slice( programCounter + caseV * EvmObjectFormat.TWO_BYTE_LENGTH, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); programCounter += caseOffset; @@ -2202,6 +2213,56 @@ private CallResult ExecuteCode 0)) + { + goto InvalidInstruction; + } + + if (!UpdateGas(GasCostOf.Callf, ref gasAvailable)) goto OutOfGas; + var index = (int)codeSection.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthUInt16(); + (int inputCount, _, int maxStackHeight) = env.CodeInfo.GetSectionMetadata(index); + + if (maxStackHeight + stack.Head > EvmObjectFormat.Eof1.MAX_STACK_HEIGHT) + { + goto StackOverflow; + } + + if (vmState.ReturnStackHead == EvmObjectFormat.Eof1.RETURN_STACK_MAX_HEIGHT) goto InvalidSubroutineEntry; + + stack.EnsureDepth(inputCount); + vmState.ReturnStack[vmState.ReturnStackHead++] = new EvmState.ReturnState + { + Index = sectionIndex, + Height = stack.Head - inputCount, + Offset = programCounter + EvmObjectFormat.TWO_BYTE_LENGTH + }; + + sectionIndex = index; + (programCounter, _) = env.CodeInfo.SectionOffset(index); + break; + } + case Instruction.RETF: + { + if (!spec.IsEofEnabled || !(env.CodeInfo.Version > 0)) + { + goto InvalidInstruction; + } + + if (!UpdateGas(GasCostOf.Retf, ref gasAvailable)) goto OutOfGas; + (_, int outputCount, _) = env.CodeInfo.GetSectionMetadata(sectionIndex); + if (vmState.ReturnStackHead == 0) + { + UpdateCurrentState(vmState, programCounter, gasAvailable, stack.Head, sectionIndex); + goto ReturnFailure; + } + + var stackFrame = vmState.ReturnStack[--vmState.ReturnStackHead]; + sectionIndex = stackFrame.Index; + programCounter = stackFrame.Offset; + break; + } default: { goto InvalidInstruction; @@ -2227,7 +2288,7 @@ private CallResult ExecuteCode(long gasAvailable, Evm }; } - private static void UpdateCurrentState(EvmState state, int pc, long gas, int stackHead) + private static void UpdateCurrentState(EvmState state, int pc, long gas, int stackHead, int sectionId) { state.ProgramCounter = pc; state.GasAvailable = gas; state.DataStackHead = stackHead; + state.FunctionIndex = sectionId; } private static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) From 62b31546adaf1cfa4c49f3189c2d51401f315900 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 18 Jan 2024 18:16:57 +0100 Subject: [PATCH 010/255] added call* opcodes to EVM --- .../Nethermind.Evm/VirtualMachine.cs | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 103271e29a8..40ebdcbf15c 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -34,6 +34,7 @@ namespace Nethermind.Evm; using System.Linq; using System.Reflection.PortableExecutable; using System.Security.Cryptography; +using DotNetty.Common.Utilities; using Int256; using Nethermind.Evm.EOF; @@ -2263,6 +2264,22 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); + if (exceptionType != EvmExceptionType.None) goto ReturnFailure; + + if (returnData is null) + { + break; + } + + goto DataReturn; + } default: { goto InvalidInstruction; @@ -2509,6 +2526,162 @@ private EvmExceptionType InstructionCall( return EvmExceptionType.None; } + [SkipLocalsInit] + private EvmExceptionType InstructionEofCall(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, + Instruction instruction, out object returnData) + where TTracingInstructions : struct, IIsTracing + where TTracingRefunds : struct, IIsTracing + { + returnData = null; + ref readonly ExecutionEnvironment env = ref vmState.Env; + + Metrics.Calls++; + + if (instruction == Instruction.EOFDELEGATECALL && !spec.DelegateCallEnabled || + instruction == Instruction.EOFSTATICCALL && !spec.StaticCallEnabled) return EvmExceptionType.BadInstruction; + + if (!stack.PopUInt256(out UInt256 gasLimit)) return EvmExceptionType.StackUnderflow; + Address codeSource = stack.PopAddress(); + if (codeSource is null) return EvmExceptionType.StackUnderflow; + + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, spec)) return EvmExceptionType.OutOfGas; + + ICodeInfo targetCodeInfo = GetCachedCodeInfo(_worldState, codeSource, spec); + + if (instruction is Instruction.EOFDELEGATECALL + && env.CodeInfo.Version != 0 + && targetCodeInfo is not EofCodeInfo) + { + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + return EvmExceptionType.None; + } + + UInt256 callValue; + switch (instruction) + { + case Instruction.EOFSTATICCALL: + callValue = UInt256.Zero; + break; + case Instruction.EOFDELEGATECALL: + callValue = env.Value; + break; + default: + if (!stack.PopUInt256(out callValue)) return EvmExceptionType.StackUnderflow; + break; + } + + UInt256 transferValue = instruction == Instruction.EOFDELEGATECALL ? UInt256.Zero : callValue; + if (!stack.PopUInt256(out UInt256 dataOffset)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 dataLength)) return EvmExceptionType.StackUnderflow; + + if (vmState.IsStatic && !transferValue.IsZero && instruction != Instruction.CALLCODE) return EvmExceptionType.StaticCallViolation; + + Address caller = instruction == Instruction.EOFDELEGATECALL ? env.Caller : env.ExecutingAccount; + Address target = instruction == Instruction.EOFCALL || instruction == Instruction.EOFSTATICCALL + ? codeSource + : env.ExecutingAccount; + + if (typeof(TLogger) == typeof(IsTracing)) + { + _logger.Trace($"caller {caller}"); + _logger.Trace($"code source {codeSource}"); + _logger.Trace($"target {target}"); + _logger.Trace($"value {callValue}"); + _logger.Trace($"transfer value {transferValue}"); + } + + long gasExtra = 0L; + + if (!transferValue.IsZero) + { + gasExtra += GasCostOf.CallValue; + } + + if (!spec.ClearEmptyAccountWhenTouched && !_state.AccountExists(target)) + { + gasExtra += GasCostOf.NewAccount; + } + else if (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && _state.IsDeadAccount(target)) + { + gasExtra += GasCostOf.NewAccount; + } + + if (!UpdateGas(spec.GetCallCost(), ref gasAvailable) || + !UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataLength) || + !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + gasLimit = (UInt256)(gasAvailable - gasAvailable / 64); + + if (gasLimit >= long.MaxValue) return EvmExceptionType.OutOfGas; + + long gasLimitUl = (long)gasLimit; + if (!UpdateGas(gasLimitUl, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + if (!transferValue.IsZero) + { + if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportExtraGasPressure(GasCostOf.CallStipend); + gasLimitUl += GasCostOf.CallStipend; + } + + if (env.CallDepth >= MaxCallDepth || + !transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) + { + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + + if (typeof(TTracingInstructions) == typeof(IsTracing)) + { + // very specific for Parity trace, need to find generalization - very peculiar 32 length... + ReadOnlyMemory memoryTrace = vmState.Memory.Inspect(in dataOffset, 32); + _txTracer.ReportMemoryChange(dataOffset, memoryTrace.Span); + } + + if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); + if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationRemainingGas(gasAvailable); + if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); + + UpdateGasUp(gasLimitUl, ref gasAvailable); + if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportGasUpdateForVmTrace(gasLimitUl, gasAvailable); + return EvmExceptionType.None; + } + + ReadOnlyMemory callData = vmState.Memory.Load(in dataOffset, dataLength); + + Snapshot snapshot = _worldState.TakeSnapshot(); + _state.SubtractFromBalance(caller, transferValue, spec); + + ExecutionEnvironment callEnv = new + ( + txExecutionContext: in env.TxExecutionContext, + callDepth: env.CallDepth + 1, + caller: caller, + codeSource: codeSource, + executingAccount: target, + transferValue: transferValue, + value: callValue, + inputData: callData, + codeInfo: GetCachedCodeInfo(_worldState, codeSource, spec) + ); + if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Tx call gas {gasLimitUl}"); + + ExecutionType executionType = GetCallExecutionType(instruction, env.TxExecutionContext.BlockExecutionContext.Header.IsPostMerge); + returnData = new EvmState( + gasLimitUl, + callEnv, + executionType, + isTopLevel: false, + snapshot, + (long)0, + (long)0, + instruction == Instruction.STATICCALL || vmState.IsStatic, + vmState, + isContinuation: false, + isCreateOnPreExistingAccount: false); + + return EvmExceptionType.None; + } + [SkipLocalsInit] private static EvmExceptionType InstructionRevert(EvmState vmState, ref EvmStack stack, ref long gasAvailable, out object returnData) where TTracing : struct, IIsTracing From a3ff60da58696c5e83426b2fa58d791cfc41fde5 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 18 Jan 2024 18:53:26 +0100 Subject: [PATCH 011/255] use ICreateInfo + CodeInfoFactory --- .../Nethermind.Evm/ExecutionEnvironment.cs | 2 +- .../Nethermind.Evm/IVirtualMachine.cs | 2 +- .../TransactionProcessor.cs | 4 +- .../Nethermind.Evm/VirtualMachine.cs | 70 +++++++++---------- 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs b/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs index a62f3198a55..613e0ef25fc 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs @@ -12,7 +12,7 @@ public readonly struct ExecutionEnvironment { public ExecutionEnvironment ( - CodeInfo codeInfo, + ICodeInfo codeInfo, Address executingAccount, Address caller, Address? codeSource, diff --git a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs index 2a8188828b3..dbe39630257 100644 --- a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs @@ -16,6 +16,6 @@ public interface IVirtualMachine TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) where TTracingActions : struct, IIsTracing; - CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec spec); + ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec spec); } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index e7db44433d1..17dab7a3ee9 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -443,7 +443,7 @@ protected virtual ExecutionEnvironment BuildExecutionEnvironment( TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, tx.Initcodes); - CodeInfo codeInfo = tx.IsContractCreation ? new(tx.Data.AsArray()) + ICodeInfo codeInfo = tx.IsContractCreation ? CodeInfoFactory.CreateCodeInfo(tx.Data.AsArray(), spec) : VirtualMachine.GetCachedCodeInfo(WorldState, recipient, spec); byte[] inputData = tx.IsMessageCall ? tx.Data.AsArray() ?? Array.Empty() : Array.Empty(); @@ -615,7 +615,7 @@ protected void PrepareAccountForContractDeployment(Address contractAddress, IRel { if (WorldState.AccountExists(contractAddress)) { - CodeInfo codeInfo = VirtualMachine.GetCachedCodeInfo(WorldState, contractAddress, spec); + ICodeInfo codeInfo = VirtualMachine.GetCachedCodeInfo(WorldState, contractAddress, spec); bool codeIsNotEmpty = codeInfo.MachineCode.Length != 0; bool accountNonceIsNotZero = WorldState.GetNonce(contractAddress) != 0; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 40ebdcbf15c..8575d3d359d 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -60,7 +60,7 @@ public VirtualMachine( } } - public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec spec) + public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec spec) => _evm.GetCachedCodeInfo(worldState, codeSource, spec); public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) @@ -160,13 +160,13 @@ internal sealed class VirtualMachine : IVirtualMachine private readonly IBlockhashProvider _blockhashProvider; private readonly ISpecProvider _specProvider; - private static readonly LruCache _codeCache = new(MemoryAllowance.CodeCacheSize, MemoryAllowance.CodeCacheSize, "VM bytecodes"); + private static readonly LruCache _codeCache = new(MemoryAllowance.CodeCacheSize, MemoryAllowance.CodeCacheSize, "VM bytecodes"); private readonly ILogger _logger; private IWorldState _worldState; private IWorldState _state; private readonly Stack _stateStack = new(); private (Address Address, bool ShouldDelete) _parityTouchBugAccount = (Address.FromNumber(3), false); - private Dictionary? _precompiles; + private Dictionary? _precompiles; private byte[] _returnDataBuffer = Array.Empty(); private ITxTracer _txTracer = NullTxTracer.Instance; @@ -479,7 +479,7 @@ private void RevertParityTouchBugAccount(IReleaseSpec spec) } } - public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) + public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) { if (codeSource.IsPrecompile(vmSpec)) { @@ -492,7 +492,7 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR } Hash256 codeHash = worldState.GetCodeHash(codeSource); - CodeInfo cachedCodeInfo = _codeCache.Get(codeHash); + ICodeInfo cachedCodeInfo = _codeCache.Get(codeHash); if (cachedCodeInfo is null) { byte[] code = worldState.GetCode(codeHash); @@ -502,7 +502,7 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR throw new NullReferenceException($"Code {codeHash} missing in the state for address {codeSource}"); } - cachedCodeInfo = new CodeInfo(code); + cachedCodeInfo = CodeInfoFactory.CreateCodeInfo(code, vmSpec); _codeCache.Set(codeHash, cachedCodeInfo); } else @@ -516,31 +516,31 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR private void InitializePrecompiledContracts() { - _precompiles = new Dictionary - { - [EcRecoverPrecompile.Address] = new(EcRecoverPrecompile.Instance), - [Sha256Precompile.Address] = new(Sha256Precompile.Instance), - [Ripemd160Precompile.Address] = new(Ripemd160Precompile.Instance), - [IdentityPrecompile.Address] = new(IdentityPrecompile.Instance), - - [Bn254AddPrecompile.Address] = new(Bn254AddPrecompile.Instance), - [Bn254MulPrecompile.Address] = new(Bn254MulPrecompile.Instance), - [Bn254PairingPrecompile.Address] = new(Bn254PairingPrecompile.Instance), - [ModExpPrecompile.Address] = new(ModExpPrecompile.Instance), - - [Blake2FPrecompile.Address] = new(Blake2FPrecompile.Instance), - - [G1AddPrecompile.Address] = new(G1AddPrecompile.Instance), - [G1MulPrecompile.Address] = new(G1MulPrecompile.Instance), - [G1MultiExpPrecompile.Address] = new(G1MultiExpPrecompile.Instance), - [G2AddPrecompile.Address] = new(G2AddPrecompile.Instance), - [G2MulPrecompile.Address] = new(G2MulPrecompile.Instance), - [G2MultiExpPrecompile.Address] = new(G2MultiExpPrecompile.Instance), - [PairingPrecompile.Address] = new(PairingPrecompile.Instance), - [MapToG1Precompile.Address] = new(MapToG1Precompile.Instance), - [MapToG2Precompile.Address] = new(MapToG2Precompile.Instance), - - [PointEvaluationPrecompile.Address] = new(PointEvaluationPrecompile.Instance), + _precompiles = new Dictionary + { + [EcRecoverPrecompile.Address] = new CodeInfo(EcRecoverPrecompile.Instance), + [Sha256Precompile.Address] = new CodeInfo(Sha256Precompile.Instance), + [Ripemd160Precompile.Address] = new CodeInfo(Ripemd160Precompile.Instance), + [IdentityPrecompile.Address] = new CodeInfo(IdentityPrecompile.Instance), + + [Bn254AddPrecompile.Address] = new CodeInfo(Bn254AddPrecompile.Instance), + [Bn254MulPrecompile.Address] = new CodeInfo(Bn254MulPrecompile.Instance), + [Bn254PairingPrecompile.Address]= new CodeInfo(Bn254PairingPrecompile.Instance), + [ModExpPrecompile.Address] = new CodeInfo(ModExpPrecompile.Instance), + + [Blake2FPrecompile.Address] = new CodeInfo(Blake2FPrecompile.Instance), + + [G1AddPrecompile.Address] = new CodeInfo(G1AddPrecompile.Instance), + [G1MulPrecompile.Address] = new CodeInfo(G1MulPrecompile.Instance), + [G1MultiExpPrecompile.Address] = new CodeInfo(G1MultiExpPrecompile.Instance), + [G2AddPrecompile.Address] = new CodeInfo(G2AddPrecompile.Instance), + [G2MulPrecompile.Address] = new CodeInfo(G2MulPrecompile.Instance), + [G2MultiExpPrecompile.Address] = new CodeInfo(G2MultiExpPrecompile.Instance), + [PairingPrecompile.Address] = new CodeInfo(PairingPrecompile.Instance), + [MapToG1Precompile.Address] = new CodeInfo(MapToG1Precompile.Instance), + [MapToG2Precompile.Address] = new CodeInfo(MapToG2Precompile.Instance), + + [PointEvaluationPrecompile.Address] = new CodeInfo(PointEvaluationPrecompile.Instance), }; } @@ -2876,11 +2876,11 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref ValueHash256 codeHash = ValueKeccak.Compute(initCode); // Prefer code from code cache (e.g. if create from a factory contract or copypasta) - if (!_codeCache.TryGet(codeHash, out CodeInfo codeInfo)) + if (!_codeCache.TryGet(codeHash, out ICodeInfo codeinfo)) { - codeInfo = new(initCode.ToArray()); + codeinfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec); // Prime the code cache as likely to be used by more txs - _codeCache.Set(codeHash, codeInfo); + _codeCache.Set(codeHash, codeinfo); } ExecutionEnvironment callEnv = new @@ -2890,7 +2890,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref caller: env.ExecutingAccount, executingAccount: contractAddress, codeSource: null, - codeInfo: codeInfo, + codeInfo: codeinfo, inputData: default, transferValue: value, value: value From f5edf56998bdf9c00cb3a2b3f0bfeb2f46a204c7 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 22 Jan 2024 17:08:51 +0100 Subject: [PATCH 012/255] initial eof contract creation implementation --- .../Nethermind.Evm/AddressExtensions.cs | 13 + .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 10 + .../Nethermind.Evm/CodeDepositHandler.cs | 34 +- .../Nethermind.Evm/CodeInfoFactory.cs | 2 +- .../EvmObjectFormat/EofCodeValidator.cs | 24 +- src/Nethermind/Nethermind.Evm/EvmState.cs | 2 + .../Nethermind.Evm/ExecutionType.cs | 12 +- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 5 +- .../Tracing/ParityStyle/ParityLikeTxTracer.cs | 10 +- .../TransactionProcessor.cs | 3 +- .../Nethermind.Evm/TxExecutionContext.cs | 4 +- .../Nethermind.Evm/VirtualMachine.cs | 446 ++++++++++++++++-- 12 files changed, 499 insertions(+), 66 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/AddressExtensions.cs b/src/Nethermind/Nethermind.Evm/AddressExtensions.cs index c52f5453184..e8536211eec 100644 --- a/src/Nethermind/Nethermind.Evm/AddressExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/AddressExtensions.cs @@ -23,7 +23,20 @@ public static Address From(Address? deployingAddress, in UInt256 nonce) return new Address(in contractAddressKeccak); } + public static Address From(Address deployingAddress, ReadOnlySpan salt, ReadOnlySpan initCode, ReadOnlySpan auxData) + { + // sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code) ++ sha3(aux_data)) + Span bytes = new byte[1 + Address.Size + 32 + salt.Length + auxData.Length]; + bytes[0] = 0xff; + deployingAddress.Bytes.CopyTo(bytes.Slice(1, 20)); + salt.CopyTo(bytes.Slice(21, salt.Length)); + ValueKeccak.Compute(initCode).BytesAsSpan.CopyTo(bytes.Slice(21 + salt.Length, 32)); + auxData.CopyTo(bytes.Slice(21 + salt.Length + 32, auxData.Length)); + ValueHash256 contractAddressKeccak = ValueKeccak.Compute(bytes); + Span addressBytes = contractAddressKeccak.BytesAsSpan[12..]; + return new Address(addressBytes.ToArray()); + } public static Address From(Address deployingAddress, ReadOnlySpan salt, ReadOnlySpan initCode) { // sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index bc56d3eb2d5..cfa35c2bee3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -45,5 +45,15 @@ private JumpDestinationAnalyzer CreateAnalyzer() { return _analyzer = new JumpDestinationAnalyzer(MachineCode); } + + public SectionHeader SectionOffset(int idx) + { + throw new NotImplementedException(); + } + + public SectionHeader ContainerOffset(int idx) + { + throw new NotImplementedException(); + } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs index 08fd7440f06..2f855c7fa21 100644 --- a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs @@ -3,6 +3,7 @@ using System; using Nethermind.Core.Specs; +using Nethermind.Evm.EOF; namespace Nethermind.Evm { @@ -17,14 +18,39 @@ public static long CalculateCost(int byteCodeLength, IReleaseSpec spec) return GasCostOf.CodeDeposit * byteCodeLength; } - public static bool CodeIsInvalid(IReleaseSpec spec, byte[] output) + + public static bool CodeIsInvalid(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion) + => !CodeIsValid(spec, code, fromVersion); + public static bool CodeIsValid(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion) + { + bool valid = true; + if (spec.IsEofEnabled) + { + //fromVersion = (execType is ExecutionType.Create1 or ExecutionType.Create2) ? fromVersion : 0; //// hmmmm + valid = IsValidWithEofRules(code.Span, fromVersion); + } + else if (spec.IsEip3541Enabled) + { + valid = IsValidWithLegacyRules(code.Span); + } + + return valid; + } + + public static bool IsValidWithLegacyRules(ReadOnlySpan code) { - return spec.IsEip3541Enabled && output.Length >= 1 && output[0] == InvalidStartingCodeByte; + return code is not [InvalidStartingCodeByte, ..]; ; } - public static bool CodeIsInvalid(IReleaseSpec spec, ReadOnlyMemory output) + public static bool IsValidWithEofRules(ReadOnlySpan code, int fromVersion) { - return spec.IsEip3541Enabled && output.Length >= 1 && output.StartsWith(InvalidStartingCodeByte); + bool isCodeEof = EvmObjectFormat.IsEof(code, out int codeVersion); + bool valid = code.Length >= 1 + && codeVersion >= fromVersion + && (isCodeEof ? // this needs test cases + EvmObjectFormat.IsValidEof(code, false, out _) : + fromVersion > 0 ? false : IsValidWithLegacyRules(code)); + return valid; } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index d26eee71a9b..e1924e8a222 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -11,7 +11,7 @@ public static class CodeInfoFactory public static ICodeInfo CreateCodeInfo(byte[] code, IReleaseSpec spec) { CodeInfo codeInfo = new(code); - return spec.IsEofEnabled && EvmObjectFormat.IsValidEof(code, out EofHeader? header) + return spec.IsEofEnabled && EvmObjectFormat.IsValidEof(code, false, out EofHeader? header) ? new EofCodeInfo(codeInfo, header.Value) : codeInfo; } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 3285e01a783..1159f95f297 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -71,7 +71,7 @@ public static bool IsEof(ReadOnlySpan container, [NotNullWhen(true)] out i public static bool IsEofn(ReadOnlySpan container, byte version) => container.Length >= MAGIC.Length + 1 && container.StartsWith(MAGIC) && container[MAGIC.Length] == version; - public static bool IsValidEof(ReadOnlySpan container, [NotNullWhen(true)] out EofHeader? header) + public static bool IsValidEof(ReadOnlySpan container, bool validateSubContainers, [NotNullWhen(true)] out EofHeader? header) { if (container.Length > VERSION_OFFSET && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) @@ -82,6 +82,28 @@ public static bool IsValidEof(ReadOnlySpan container, [NotNullWhen(true)] { return true; } + + if(validateSubContainers && header?.ContainerSection?.Count > 0) + { + int containerSize = header.Value.ContainerSection.Value.Count; + byte[][] containers = ArrayPool.Shared.Rent(containerSize); + + for (int i = 0; i < containerSize; i++) + { + containers[i] = container.Slice(header.Value.ContainerSection.Value.Start + header.Value.ContainerSection.Value[i].Start, header.Value.ContainerSection.Value[i].Size).ToArray(); + } + + foreach (var subcontainer in containers) + { + if(!IsValidEof(subcontainer, validateSubContainers, out _)) + { + return false; + } + } + return true; + } + + } header = null; diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index c0caa72e920..ee11eb9ca14 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -223,6 +223,8 @@ public Address From case ExecutionType.CALLCODE: case ExecutionType.CREATE: case ExecutionType.CREATE2: + case ExecutionType.CREATE3: + case ExecutionType.CREATE4: case ExecutionType.TRANSACTION: return Env.Caller; case ExecutionType.DELEGATECALL: diff --git a/src/Nethermind/Nethermind.Evm/ExecutionType.cs b/src/Nethermind/Nethermind.Evm/ExecutionType.cs index 95045606b06..68786733c8a 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionType.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionType.cs @@ -7,10 +7,16 @@ 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.CREATE3 or ExecutionType.CREATE4; // did not want to use flags here specifically [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAnyCreate(this ExecutionType executionType) => - executionType is ExecutionType.CREATE or ExecutionType.CREATE2; + IsAnyCreateLegacy(executionType) || IsAnyCreateEof(executionType); } // ReSharper disable InconsistentNaming IdentifierTypo @@ -22,7 +28,9 @@ public enum ExecutionType CALLCODE, DELEGATECALL, CREATE, - CREATE2 + CREATE2, + CREATE3, + CREATE4, } // ReSharper restore IdentifierTypo InconsistentNaming } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index e77f64ddbb1..806d6dd42e6 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using Nethermind.Evm.EOF; using Nethermind.Evm.Precompiles; namespace Nethermind.Evm.CodeAnalysis; @@ -17,8 +18,8 @@ public interface ICodeInfo ReadOnlyMemory CodeSection => MachineCode; ReadOnlyMemory DataSection => Memory.Empty; ReadOnlyMemory ContainerSection => Memory.Empty; - (int start, int size) SectionOffset(int idx) => idx == 0 ? (0, MachineCode.Length) : throw new ArgumentOutOfRangeException(); - (int start, int size) ContainerOffset(int idx) => idx == 0 ? (0, 0) : throw new ArgumentOutOfRangeException(); + SectionHeader SectionOffset(int idx); + SectionHeader ContainerOffset(int idx); (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); bool ValidateJump(int destination, bool isSubroutine); } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs index 908a90527d8..6f4661b59b4 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs @@ -73,8 +73,9 @@ private static string GetCallType(ExecutionType executionType) case ExecutionType.TRANSACTION: return "call"; case ExecutionType.CREATE: - return "create"; case ExecutionType.CREATE2: + case ExecutionType.CREATE3: + case ExecutionType.CREATE4: return "create"; case ExecutionType.CALL: return "call"; @@ -96,8 +97,9 @@ private static string GetActionType(ExecutionType executionType) case ExecutionType.TRANSACTION: return "call"; case ExecutionType.CREATE: - return "create"; case ExecutionType.CREATE2: + case ExecutionType.CREATE3: + case ExecutionType.CREATE4: return "create"; case ExecutionType.CALL: return "call"; @@ -421,6 +423,10 @@ public override void ReportAction(long gas, UInt256 value, Address from, Address return "create"; case ExecutionType.CREATE2: return "create2"; + case ExecutionType.CREATE3: + return "create3"; + case ExecutionType.CREATE4: + return "create4"; default: return null; } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 17dab7a3ee9..634044c4bac 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -545,7 +545,8 @@ protected virtual bool ExecuteEvmCall( throw new OutOfGasException(); } - if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output)) + // is the new txType considered a contractCreation if it needs CREATE4 to function as such? + if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output, 0)) { throw new InvalidCodeException(); } diff --git a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs index 7223b7a23c8..6be5f9fb2da 100644 --- a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs +++ b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs @@ -12,7 +12,7 @@ public readonly struct TxExecutionContext public Address Origin { get; } public UInt256 GasPrice { get; } public byte[][]? BlobVersionedHashes { get; } - public byte[][]? Initicodes { get; } + public byte[][]? InitCodes { get; } public TxExecutionContext(in BlockExecutionContext blockExecutionContext, Address origin, in UInt256 gasPrice, byte[][] blobVersionedHashes, byte[][] initicodes) { @@ -20,7 +20,7 @@ public TxExecutionContext(in BlockExecutionContext blockExecutionContext, Addres Origin = origin; GasPrice = gasPrice; BlobVersionedHashes = blobVersionedHashes; - Initicodes = initicodes; + InitCodes = initicodes; } } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8575d3d359d..4efd29953b4 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -31,12 +31,16 @@ namespace Nethermind.Evm; +using System.ComponentModel; using System.Linq; using System.Reflection.PortableExecutable; using System.Security.Cryptography; using DotNetty.Common.Utilities; using Int256; using Nethermind.Evm.EOF; +using Nethermind.Evm.Tracing.GethStyle.JavaScript; +using Org.BouncyCastle.Asn1.X509; +using SectionHeader = EOF.SectionHeader; public class VirtualMachine : IVirtualMachine { @@ -87,42 +91,56 @@ public static CallResult InvalidInstructionException public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); - public static CallResult Empty => new(Array.Empty(), null); + public static CallResult Empty(int fromVersion) => new(Array.Empty(), null, fromVersion); public CallResult(EvmState stateToExecute) { StateToExecute = stateToExecute; - Output = Array.Empty(); + Output = (null, Array.Empty()); PrecompileSuccess = null; ShouldRevert = false; ExceptionType = EvmExceptionType.None; } - private CallResult(EvmExceptionType exceptionType) + public CallResult(byte[] output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) { StateToExecute = null; - Output = StatusCode.FailureBytes; - PrecompileSuccess = null; - ShouldRevert = false; + Output = (null, output); + PrecompileSuccess = precompileSuccess; + ShouldRevert = shouldRevert; ExceptionType = exceptionType; + FromVersion = fromVersion; } - public CallResult(byte[] output, bool? precompileSuccess, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + public CallResult(int containerIndex, byte[] output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) { StateToExecute = null; - Output = output; + Output = (containerIndex, output); PrecompileSuccess = precompileSuccess; ShouldRevert = shouldRevert; ExceptionType = exceptionType; + FromVersion = fromVersion; + } + + private CallResult(EvmExceptionType exceptionType) + { + StateToExecute = null; + Output = (null, StatusCode.FailureBytes); + PrecompileSuccess = null; + ShouldRevert = false; + ExceptionType = exceptionType; } + public EvmState? StateToExecute { get; } - public byte[] Output { get; } + public (int? ContainerIndex, byte[] Bytes) Output { get; } public EvmExceptionType ExceptionType { get; } public bool ShouldRevert { get; } public bool? PrecompileSuccess { get; } // TODO: check this behaviour as it seems it is required and previously that was not the case public bool IsReturn => StateToExecute is null; public bool IsException => ExceptionType != EvmExceptionType.None; + + public int FromVersion { get; } } public interface IIsTracing { } @@ -283,7 +301,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl { if (typeof(TTracingActions) == typeof(IsTracing)) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(callResult.Output.Length, spec); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(callResult.Output.Bytes.Length, spec); if (callResult.IsException) { @@ -294,7 +312,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl _txTracer.ReportActionRevert(currentState.ExecutionType.IsAnyCreate() ? currentState.GasAvailable - codeDepositGasCost : currentState.GasAvailable, - callResult.Output); + callResult.Output.Bytes); } else { @@ -306,11 +324,11 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } else { - _txTracer.ReportActionEnd(currentState.GasAvailable, currentState.To, callResult.Output); + _txTracer.ReportActionEnd(currentState.GasAvailable, currentState.To, callResult.Output.Bytes); } } // Reject code starting with 0xEF if EIP-3541 is enabled. - else if (currentState.ExecutionType.IsAnyCreate() && CodeDepositHandler.CodeIsInvalid(spec, callResult.Output)) + else if (currentState.ExecutionType.IsAnyCreate() && CodeDepositHandler.CodeIsInvalid(spec, callResult.Output.Bytes, callResult.FromVersion)) { _txTracer.ReportActionError(EvmExceptionType.InvalidCode); } @@ -318,7 +336,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl { if (currentState.ExecutionType.IsAnyCreate()) { - _txTracer.ReportActionEnd(currentState.GasAvailable - codeDepositGasCost, currentState.To, callResult.Output); + _txTracer.ReportActionEnd(currentState.GasAvailable - codeDepositGasCost, currentState.To, callResult.Output.Bytes); } else { @@ -329,7 +347,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } return new TransactionSubstate( - callResult.Output, + callResult.Output.Bytes, currentState.Refund, (IReadOnlyCollection
)currentState.DestroyList, (IReadOnlyCollection)currentState.Logs, @@ -353,46 +371,151 @@ public TransactionSubstate Run(EvmState state, IWorldState worl previousCallOutputDestination = UInt256.Zero; _returnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; - - long codeDepositGasCost = CodeDepositHandler.CalculateCost(callResult.Output.Length, spec); - bool invalidCode = CodeDepositHandler.CodeIsInvalid(spec, callResult.Output); - if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) + if (previousState.ExecutionType.IsAnyCreateLegacy()) { - _state.InsertCode(callCodeOwner, callResult.Output, spec); - currentState.GasAvailable -= codeDepositGasCost; + long codeDepositGasCost = CodeDepositHandler.CalculateCost(callResult.Output.Bytes.Length, spec); + bool invalidCode = !CodeDepositHandler.CodeIsValid(spec, callResult.Output.Bytes, callResult.FromVersion); + if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) + { + _state.InsertCode(callCodeOwner, callResult.Output.Bytes, spec); + currentState.GasAvailable -= codeDepositGasCost; - if (typeof(TTracingActions) == typeof(IsTracing)) + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); + } + } + else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) + { + currentState.GasAvailable -= gasAvailableForCodeDeposit; + worldState.Restore(previousState.Snapshot); + if (!previousState.IsCreateOnPreExistingAccount) + { + _state.DeleteAccount(callCodeOwner); + } + + previousCallResult = BytesZero; + previousStateSucceeded = false; + + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); + } + } + else if (_txTracer.IsTracingActions) { - _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output); + _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); } } - else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) + else if (previousState.ExecutionType.IsAnyCreateEof()) { - currentState.GasAvailable -= gasAvailableForCodeDeposit; - worldState.Restore(previousState.Snapshot); - if (!previousState.IsCreateOnPreExistingAccount) + int containerIndex = callResult.Output.ContainerIndex.Value; + byte[] auxExtraData = callResult.Output.Bytes; + (int start, int size) = previousState.Env.CodeInfo.ContainerOffset(containerIndex); + ReadOnlySpan container = previousState.Env.CodeInfo.ContainerSection.Slice(start, size).Span; + bool invalidCode = !EvmObjectFormat.TryExtractHeader(container, out EofHeader? header); + byte[] bytecodeResultArray = null; + + if (!invalidCode) { - _state.DeleteAccount(callCodeOwner); + Span bytecodeResult = new byte[container.Length + header.Value.DataSection.Size]; + + // copy magic eof prefix + int movingOffset = 0; + ReadOnlySpan magicSpan = container.Slice(0, EvmObjectFormat.MAGIC.Length + EvmObjectFormat.ONE_BYTE_LENGTH); + magicSpan.CopyTo(bytecodeResult); + movingOffset += magicSpan.Length; + + // copy typesection header + ReadOnlySpan typesectionHeaderSpan = container.Slice(header.Value.TypeSection.Start, header.Value.TypeSection.Size); + typesectionHeaderSpan.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += typesectionHeaderSpan.Length; + + // copy codesection header + ReadOnlySpan codesectionHeaderSpan = container.Slice(header.Value.CodeSections.Start, header.Value.CodeSections.Size); + codesectionHeaderSpan.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += codesectionHeaderSpan.Length; + + + // copy containersection header + ReadOnlySpan containerectionHeaderSpan = container.Slice(header.Value.ContainerSection.Value.Start, header.Value.ContainerSection.Value.Size); + containerectionHeaderSpan.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += containerectionHeaderSpan.Length; + + // copy datasection header + bytecodeResult[movingOffset++] = (byte)EvmObjectFormat.Eof1.Separator.KIND_DATA; + byte[] newDataSectionLength = (auxExtraData.Length + header.Value.DataSection.Size).ToBigEndianByteArray(); + newDataSectionLength.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += newDataSectionLength.Length; + + bytecodeResult[movingOffset++] = (byte)EvmObjectFormat.Eof1.Separator.TERMINATOR; + + // copy type section + ReadOnlySpan typesectionSpan = container.Slice(header.Value.TypeSection.Start, header.Value.TypeSection.Size); + typesectionSpan.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += typesectionSpan.Length; + + // copy code section + ReadOnlySpan codesectionSpan = container.Slice(header.Value.CodeSections[0].Start, header.Value.CodeSections.Size); + codesectionSpan.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += codesectionSpan.Length; + + // copy container section + ReadOnlySpan containersectionSpan = container.Slice(header.Value.ContainerSection?[0].Start ?? 0, header.Value.ContainerSection?.Size ?? 0); + containersectionSpan.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += containersectionSpan.Length; + + // copy data section + ReadOnlySpan datasectionSpan = container.Slice(header.Value.DataSection.Start, header.Value.DataSection.Size); + datasectionSpan.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += datasectionSpan.Length; + + // copy aux data to dataSection + auxExtraData.CopyTo(bytecodeResult.Slice(movingOffset)); + movingOffset += auxExtraData.Length; + + bytecodeResultArray = bytecodeResult.ToArray(); } - previousCallResult = BytesZero; - previousStateSucceeded = false; + long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray.Length, spec); + if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) + { + _state.InsertCode(callCodeOwner, callResult.Output.Bytes, spec); + currentState.GasAvailable -= codeDepositGasCost; - if (typeof(TTracingActions) == typeof(IsTracing)) + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); + } + } + else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) + { + currentState.GasAvailable -= gasAvailableForCodeDeposit; + worldState.Restore(previousState.Snapshot); + if (!previousState.IsCreateOnPreExistingAccount) + { + _state.DeleteAccount(callCodeOwner); + } + + previousCallResult = BytesZero; + previousStateSucceeded = false; + + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); + } + } + else if (_txTracer.IsTracingActions) { - _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); + _txTracer.ReportActionEnd(0L, callCodeOwner, callResult.Output.Bytes); } } - else if (typeof(TTracingActions) == typeof(IsTracing)) - { - _txTracer.ReportActionEnd(0L, callCodeOwner, callResult.Output); - } } else { - _returnDataBuffer = callResult.Output; + _returnDataBuffer = callResult.Output.Bytes; previousCallResult = callResult.PrecompileSuccess.HasValue ? (callResult.PrecompileSuccess.Value ? StatusCode.SuccessBytes : StatusCode.FailureBytes) : StatusCode.SuccessBytes; - previousCallOutput = callResult.Output.AsSpan().SliceWithZeroPadding(0, Math.Min(callResult.Output.Length, (int)previousState.OutputLength)); + previousCallOutput = callResult.Output.Bytes.AsSpan().SliceWithZeroPadding(0, Math.Min(callResult.Output.Bytes.Length, (int)previousState.OutputLength)); previousCallOutputDestination = (ulong)previousState.OutputDestination; if (previousState.IsPrecompile) { @@ -417,15 +540,15 @@ public TransactionSubstate Run(EvmState state, IWorldState worl else { worldState.Restore(previousState.Snapshot); - _returnDataBuffer = callResult.Output; + _returnDataBuffer = callResult.Output.Bytes; previousCallResult = StatusCode.FailureBytes; - previousCallOutput = callResult.Output.AsSpan().SliceWithZeroPadding(0, Math.Min(callResult.Output.Length, (int)previousState.OutputLength)); + previousCallOutput = callResult.Output.Bytes.AsSpan().SliceWithZeroPadding(0, Math.Min(callResult.Output.Bytes.Length, (int)previousState.OutputLength)); previousCallOutputDestination = (ulong)previousState.OutputDestination; if (typeof(TTracingActions) == typeof(IsTracing)) { - _txTracer.ReportActionRevert(previousState.GasAvailable, callResult.Output); + _txTracer.ReportActionRevert(previousState.GasAvailable, callResult.Output.Bytes); } } } @@ -672,7 +795,7 @@ private CallResult ExecutePrecompile(EvmState state, IReleaseSpec spec) try { (ReadOnlyMemory output, bool success) = precompile.Run(callData, spec); - CallResult callResult = new(output.ToArray(), success, !success); + CallResult callResult = new(output.ToArray(), success, 0, !success); return callResult; } catch (DllNotFoundException exception) @@ -683,7 +806,7 @@ private CallResult ExecutePrecompile(EvmState state, IReleaseSpec spec) catch (Exception exception) { if (_logger.IsError) _logger.Error($"Precompiled contract ({precompile.GetType()}) execution exception", exception); - CallResult callResult = new(Array.Empty(), false, true); + CallResult callResult = new(Array.Empty(), false, 0, true); return callResult; } } @@ -763,7 +886,7 @@ private CallResult ExecuteCall(EvmState vmState, byte[]? p ExecuteCode(vmState, ref stack, gasAvailable, spec); } Empty: - return CallResult.Empty; + return CallResult.Empty(vmState.Env.CodeInfo.Version); OutOfGas: return CallResult.OutOfGasException; } @@ -817,6 +940,10 @@ private CallResult ExecuteCode auxData = Span.Empty; + if (aux_data_size > UInt256.Zero) + { + if (!UpdateMemoryCost(vmState, ref gasAvailable, in aux_data_offset, aux_data_size)) goto OutOfGas; + auxData = env.InputData.Slice((int)aux_data_offset, (int)aux_data_size).Span; + } + + return new CallResult(sectionIdx, auxData.ToArray(), null, env.CodeInfo.Version); + } default: { goto InvalidInstruction; @@ -2309,7 +2476,7 @@ private CallResult ExecuteCode(EvmState vmState, ref return EvmExceptionType.None; } + [SkipLocalsInit] + private (EvmExceptionType exceptionType, EvmState? callState) InstructionEofCreate(EvmState vmState, ref ReadOnlySpan codeSection, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) + where TTracing : struct, IIsTracing + { + ref readonly ExecutionEnvironment env = ref vmState.Env; + + var currentContext = instruction == Instruction.CREATE3 ? ExecutionType.CREATE3: ExecutionType.CREATE4; + if (!UpdateGas(currentContext == ExecutionType.CREATE3 ? GasCostOf.Create3 : GasCostOf.Create4, ref gasAvailable)) // still undecided in EIP + return (EvmExceptionType.OutOfGas, null); + + if (!stack.PopUInt256(out UInt256 value) || + !stack.PopUInt256(out UInt256 salt) || + !stack.PopUInt256(out UInt256 dataOffset) || + !stack.PopUInt256(out UInt256 dataSize)) + return (EvmExceptionType.StackUnderflow, null); + + ReadOnlyMemory initCode = ReadOnlyMemory.Empty; + switch(instruction) + { + case Instruction.CREATE3 : + { + int initCodeIdx = codeSection[vmState.ProgramCounter]; + SectionHeader containerSection = env.CodeInfo.ContainerOffset(initCodeIdx); + initCode = env.CodeInfo.ContainerSection[containerSection.Start..containerSection.Size]; + break; + } + case Instruction.CREATE4 : + { + byte[] initContainerHash = stack.PopWord256().ToArray(); + var initcode = env.TxExecutionContext.InitCodes.First( + initcode => initContainerHash == Keccak.Compute(initcode).Bytes + ); + if(initcode is null) + { + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + return (EvmExceptionType.None, null); + } else + { + initCode = initcode; + } + break; + } + }; + + //EIP-3860 + if (spec.IsEip3860Enabled) + { + if (initCode.Length > spec.MaxInitCodeSize) return (EvmExceptionType.InvalidCode, null); + } + + long gasCost = (instruction is Instruction.CREATE4 && spec.IsEip3860Enabled ? GasCostOf.InitCodeWord * EvmPooledMemory.Div32Ceiling((UInt256)initCode.Length) : 0) + + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initCode.Length); + + if (!UpdateGas(gasCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); + + // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? + if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients + { + // TODO: need a test for this + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + return (EvmExceptionType.None, null); + } + + if (!EvmObjectFormat.IsValidEof(initCode.Span, instruction is Instruction.CREATE4, out _)) + { + // handle invalid Eof code + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + return (EvmExceptionType.None, null); + } + + UInt256 balance = _state.GetBalance(env.ExecutingAccount); + if (value > balance) + { + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + return (EvmExceptionType.None, null); + } + + UInt256 accountNonce = _state.GetNonce(env.ExecutingAccount); + UInt256 maxNonce = ulong.MaxValue; + if (accountNonce >= maxNonce) + { + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + return (EvmExceptionType.None, null); + } + + if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState.Memory?.Size ?? 0); + // todo: === below is a new call - refactor / move + + long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; + if (!UpdateGas(callGas, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); + + Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt.ToBytes(), initCode.Span, env.InputData.Span); + + if (spec.UseHotAndColdStorage) + { + // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 + vmState.WarmUp(contractAddress); + } + + _state.IncrementNonce(env.ExecutingAccount); + + Snapshot snapshot = _worldState.TakeSnapshot(); + + bool accountExists = _state.AccountExists(contractAddress); + if (accountExists && (GetCachedCodeInfo(_worldState, contractAddress, spec).MachineCode.Length != 0 || + _state.GetNonce(contractAddress) != 0)) + { + /* we get the snapshot before this as there is a possibility with that we will touch an empty account and remove it even if the REVERT operation follows */ + if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + return (EvmExceptionType.None, null); + } + + if (accountExists) + { + _state.UpdateStorageRoot(contractAddress, Keccak.EmptyTreeHash); + } + else if (_state.IsDeadAccount(contractAddress)) + { + _state.ClearStorage(contractAddress); + } + + _state.SubtractFromBalance(env.ExecutingAccount, value, spec); + + ValueHash256 codeHash = ValueKeccak.Compute(initCode.Span); + // Prefer code from code cache (e.g. if create from a factory contract or copypasta) + if (!_codeCache.TryGet(codeHash, out ICodeInfo codeinfo)) + { + codeinfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec); + // Prime the code cache as likely to be used by more txs + _codeCache.Set(codeHash, codeinfo); + } + + ExecutionEnvironment callEnv = new + ( + txExecutionContext: in env.TxExecutionContext, + callDepth: env.CallDepth + 1, + caller: env.ExecutingAccount, + executingAccount: contractAddress, + codeSource: null, + codeInfo: codeinfo, + inputData: default, + transferValue: value, + value: value + ); + EvmState callState = new( + callGas, + callEnv, + instruction switch + { + Instruction.CREATE3 => ExecutionType.CREATE3, + Instruction.CREATE4 => ExecutionType.CREATE4, + _ => throw new UnreachableException() + }, + false, + snapshot, + 0L, + 0L, + vmState.IsStatic, + vmState, + false, + accountExists); + + return (EvmExceptionType.None, callState); + } + [SkipLocalsInit] private (EvmExceptionType exceptionType, EvmState? callState) InstructionCreate(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) where TTracing : struct, IIsTracing @@ -2898,7 +3237,12 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref EvmState callState = new( callGas, callEnv, - instruction == Instruction.CREATE2 ? ExecutionType.CREATE2 : ExecutionType.CREATE, + instruction switch + { + Instruction.CREATE => ExecutionType.CREATE, + Instruction.CREATE2 => ExecutionType.CREATE2, + _ => throw new UnreachableException() + }, false, snapshot, 0L, @@ -3197,19 +3541,19 @@ private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExc private static ExecutionType GetCallExecutionType(Instruction instruction, bool isPostMerge = false) { ExecutionType executionType; - if (instruction == Instruction.CALL) + if (instruction is Instruction.CALL or Instruction.EOFCALL) { executionType = ExecutionType.CALL; } - else if (instruction == Instruction.DELEGATECALL) + else if (instruction is Instruction.DELEGATECALL or Instruction.EOFDELEGATECALL) { executionType = ExecutionType.DELEGATECALL; } - else if (instruction == Instruction.STATICCALL) + else if (instruction is Instruction.STATICCALL or Instruction.EOFSTATICCALL) { executionType = ExecutionType.STATICCALL; } - else if (instruction == Instruction.CALLCODE) + else if (instruction is Instruction.CALLCODE) { executionType = ExecutionType.CALLCODE; } From 9b894844a51515e35d7b6e03b3f25f5893c4d5ea Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 22 Jan 2024 23:25:31 +0100 Subject: [PATCH 013/255] fix Create substate handling --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 4efd29953b4..5f47c310e01 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -480,12 +480,12 @@ public TransactionSubstate Run(EvmState state, IWorldState worl long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray.Length, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { - _state.InsertCode(callCodeOwner, callResult.Output.Bytes, spec); + _state.InsertCode(callCodeOwner, bytecodeResultArray, spec); currentState.GasAvailable -= codeDepositGasCost; if (_txTracer.IsTracingActions) { - _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); + _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, bytecodeResultArray); } } else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) @@ -507,7 +507,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } else if (_txTracer.IsTracingActions) { - _txTracer.ReportActionEnd(0L, callCodeOwner, callResult.Output.Bytes); + _txTracer.ReportActionEnd(0L, callCodeOwner, bytecodeResultArray); } } } From 845c86b1a01873f58bbe3b67a955da8e51074615 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 23 Jan 2024 11:55:23 +0100 Subject: [PATCH 014/255] - Added non-returnning check to CALLF - added some missing ArrayPool.Return calls --- .../EvmObjectFormat/EofCodeValidator.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 1159f95f297..42fc29a37ef 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -97,9 +97,11 @@ public static bool IsValidEof(ReadOnlySpan container, bool validateSubCont { if(!IsValidEof(subcontainer, validateSubContainers, out _)) { + ArrayPool.Shared.Return(containers); return false; } } + ArrayPool.Shared.Return(containers); return true; } @@ -391,11 +393,16 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header) ReadOnlySpan code = container.Slice(codeSectionStartOffset, codeSectionSize); if (!ValidateInstructions(sectionIdx, isNonReturning, typesection, code, header, validationQueue, out ushort jumpsCount)) { + ArrayPool.Shared.Return(visitedSections); return false; } } - return visitedSections[..header.CodeSections.Count].All(id => id); + var HasNoNonReachableCodeSections = visitedSections[..header.CodeSections.Count].All(id => id); + ArrayPool.Shared.Return(visitedSections); + + return HasNoNonReachableCodeSections; + } bool ValidateTypeSection(ReadOnlySpan types) @@ -498,9 +505,6 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan Date: Wed, 24 Jan 2024 13:49:30 +0100 Subject: [PATCH 015/255] applied suggested refactors --- .../EvmObjectFormat/EofCodeValidator.cs | 27 +++++++------------ .../EvmObjectFormat/EofHeader.cs | 27 +++++++++++++++++++ .../Nethermind.Evm/VirtualMachine.cs | 4 +-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 42fc29a37ef..b5f77eacbf6 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -80,32 +80,23 @@ public static bool IsValidEof(ReadOnlySpan container, bool validateSubCont EofHeader h = header.Value; if (handler.ValidateBody(container, h)) { - return true; - } - - if(validateSubContainers && header?.ContainerSection?.Count > 0) - { - int containerSize = header.Value.ContainerSection.Value.Count; - byte[][] containers = ArrayPool.Shared.Rent(containerSize); - - for (int i = 0; i < containerSize; i++) + if(validateSubContainers && header?.ContainerSection?.Count > 0) { - containers[i] = container.Slice(header.Value.ContainerSection.Value.Start + header.Value.ContainerSection.Value[i].Start, header.Value.ContainerSection.Value[i].Size).ToArray(); - } + int containerSize = header.Value.ContainerSection.Value.Count; - foreach (var subcontainer in containers) - { - if(!IsValidEof(subcontainer, validateSubContainers, out _)) + for (int i = 0; i < containerSize; i++) { - ArrayPool.Shared.Return(containers); - return false; + ReadOnlySpan subContainer = container.Slice(header.Value.ContainerSection.Value.Start + header.Value.ContainerSection.Value[i].Start, header.Value.ContainerSection.Value[i].Size); + if(!IsValidEof(subContainer, validateSubContainers, out _)) + { + return false; + } } + return true; } - ArrayPool.Shared.Return(containers); return true; } - } header = null; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index 617d834a6d7..e498c6436f8 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -28,6 +28,33 @@ public readonly record struct CompoundSectionHeader(int Start, int[] SubSections public int Size => EndOffset - Start; public int Count => SubSectionsSizes.Length; + /* + private readonly int[] subSectionsSizesAcc; + private readonly int[] SubSectionsSizesAcc + { + init + { + if(subSectionsSizesAcc is null) { + subSectionsSizesAcc = new int[SubSectionsSizes.Length]; + } + + for (var i = 0; i < SubSectionsSizes.Length; i++) + { + if(i == 0) + { + subSectionsSizesAcc[i] = 0; + } else + { + subSectionsSizesAcc[i] = subSectionsSizesAcc[i - 1] + SubSectionsSizes[i]; + } + } + } + + get => subSectionsSizesAcc; + } + + public SectionHeader this[int i] => new SectionHeader(Start: SubSectionsSizesAcc[i], Size: (ushort)SubSectionsSizes[i]); + */ // returns a subsection with localized indexing [0, size] ==> 0 is local to the section not the entire bytecode public SectionHeader this[int i] => new SectionHeader(Start: SubSectionsSizes[..i].Sum(), Size: (ushort)SubSectionsSizes[i]); } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d40526ef03b..f7e7ba7c1be 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -412,7 +412,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl bool invalidCode = !CodeDepositHandler.CodeIsValid(spec, callResult.Output.Bytes, callResult.FromVersion); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { - _state.InsertCode(callCodeOwner, callResult.Output.Bytes, spec); + InsertCode(callResult.Output.Bytes, callCodeOwner, spec); currentState.GasAvailable -= codeDepositGasCost; if (_txTracer.IsTracingActions) @@ -515,7 +515,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray.Length, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { - _state.InsertCode(callCodeOwner, bytecodeResultArray, spec); + InsertCode(bytecodeResultArray, callCodeOwner, spec); currentState.GasAvailable -= codeDepositGasCost; if (_txTracer.IsTracingActions) From 70d66a475d32ce3d2a6d208cb68ef04fa804f0c9 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 24 Jan 2024 14:15:29 +0100 Subject: [PATCH 016/255] Added Data* opcodes --- .../Nethermind.Evm/VirtualMachine.cs | 124 +++++++++++++++--- 1 file changed, 103 insertions(+), 21 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f7e7ba7c1be..3615362bb0a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1596,30 +1596,47 @@ private CallResult ExecuteCode _returnDataBuffer.Length) + { + goto AccessViolation; + } - if (UInt256.AddOverflow(result, b, out c) || c > _returnDataBuffer.Length) - { - goto AccessViolation; - } + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, 32)) goto OutOfGas; - if (!result.IsZero) + slice = _returnDataBuffer.AsSpan().SliceWithZeroPadding(a, 32); + stack.PushBytes(slice); + } + else { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, result)) goto OutOfGas; + if (!spec.ReturnDataOpcodesEnabled) goto InvalidInstruction; - slice = _returnDataBuffer.AsSpan().SliceWithZeroPadding(b, (int)result); - vmState.Memory.Save(in a, in slice); - if (typeof(TTracingInstructions) == typeof(IsTracing)) + if (!stack.PopUInt256(out a)) goto StackUnderflow; + if (!stack.PopUInt256(out b)) goto StackUnderflow; + if (!stack.PopUInt256(out result)) goto StackUnderflow; + gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result); + + if (UInt256.AddOverflow(result, b, out c) || c > _returnDataBuffer.Length) { - _txTracer.ReportMemoryChange((long)a, in slice); + goto AccessViolation; } - } + if (!result.IsZero) + { + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, result)) goto OutOfGas; + + slice = _returnDataBuffer.AsSpan().SliceWithZeroPadding(b, (int)result); + vmState.Memory.Save(in a, in slice); + if (typeof(TTracingInstructions) == typeof(IsTracing)) + { + _txTracer.ReportMemoryChange((long)a, in slice); + } + } + } break; } case Instruction.BLOCKHASH: @@ -2456,18 +2473,83 @@ private CallResult ExecuteCode auxData = Span.Empty; - if (aux_data_size > UInt256.Zero) + if (b > UInt256.Zero) { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in aux_data_offset, aux_data_size)) goto OutOfGas; - auxData = env.InputData.Slice((int)aux_data_offset, (int)aux_data_size).Span; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; + auxData = vmState.Memory.LoadSpan(a, b); } return new CallResult(sectionIdx, auxData.ToArray(), null, env.CodeInfo.Version); } + case Instruction.DATASIZE: + { + if (!spec.IsEofEnabled|| env.CodeInfo.Version == 0) + goto InvalidInstruction; + + if (!UpdateGas(GasCostOf.DataSize, ref gasAvailable)) goto OutOfGas; + + stack.PushUInt32(dataSection.Length); + break; + } + case Instruction.DATALOAD: + { + if (!spec.IsEofEnabled|| env.CodeInfo.Version == 0) + goto InvalidInstruction; + + if (!UpdateGas(GasCostOf.DataLoad, ref gasAvailable)) goto OutOfGas; + + stack.PopUInt256(out a); + ZeroPaddedSpan zpbytes = dataSection.SliceWithZeroPadding(a, 32); + stack.PushBytes(zpbytes); + break; + } + case Instruction.DATALOADN: + { + if (!spec.IsEofEnabled|| env.CodeInfo.Version == 0) + goto InvalidInstruction; + + if (!UpdateGas(GasCostOf.DataLoadN, ref gasAvailable)) goto OutOfGas; + + var offset = codeSection.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthUInt16(); + ZeroPaddedSpan zpbytes = dataSection.SliceWithZeroPadding(offset, 32); + stack.PushBytes(zpbytes); + + programCounter += EvmObjectFormat.TWO_BYTE_LENGTH; + break; + } + case Instruction.DATACOPY: + { + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) + goto InvalidInstruction; + + + if (!UpdateGas(GasCostOf.DataCopy, ref gasAvailable)) goto OutOfGas; + + stack.PopUInt256(out UInt256 memOffset); + stack.PopUInt256(out UInt256 offset); + stack.PopUInt256(out UInt256 size); + + if (size > UInt256.Zero) + { + gasAvailable -= GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size); + if (!UpdateGas(gasAvailable, ref gasAvailable) || + !UpdateMemoryCost(vmState, ref gasAvailable, in memOffset, size)) + goto OutOfGas; + + ZeroPaddedSpan dataSectionSlice = dataSection.SliceWithZeroPadding(offset, (int)size); + vmState.Memory.Save(in memOffset, dataSectionSlice); + if (_txTracer.IsTracingInstructions) + { + _txTracer.ReportMemoryChange((long)memOffset, dataSectionSlice); + } + } + + break; + } default: { goto InvalidInstruction; From d8edb9eae937243790eee4162020b8c6e17cad9e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 24 Jan 2024 14:19:40 +0100 Subject: [PATCH 017/255] name fix --- src/Nethermind/Nethermind.Core/Extensions/Bytes.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index 6941d471606..fc6b8a5c1a8 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -417,9 +417,9 @@ public static ushort ReadEthUInt16LittleEndian(this Span bytes) return BinaryPrimitives.ReadUInt16LittleEndian(bytes); } - Span fourBytes = stackalloc byte[2]; - bytes.CopyTo(fourBytes[(2 - bytes.Length)..]); - return BinaryPrimitives.ReadUInt16LittleEndian(fourBytes); + Span twoBytes = stackalloc byte[2]; + bytes.CopyTo(twoBytes[(2 - bytes.Length)..]); + return BinaryPrimitives.ReadUInt16LittleEndian(twoBytes); } public static uint ReadEthUInt32(this Span bytes) From a37a8d51673ca9b995616670ed9b3c8aa9c6dd19 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 24 Jan 2024 14:29:22 +0100 Subject: [PATCH 018/255] applied simple refactor --- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 44 ++++--------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index e86212c1205..fcee5f18ae5 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -8,13 +8,6 @@ namespace Nethermind.Evm; public static class BitmapHelper { - private const ushort Set2BitsMask = 0b1100_0000_0000_0000; - private const ushort Set3BitsMask = 0b1110_0000_0000_0000; - private const ushort Set4BitsMask = 0b1111_0000_0000_0000; - private const ushort Set5BitsMask = 0b1111_1000_0000_0000; - private const ushort Set6BitsMask = 0b1111_1100_0000_0000; - private const ushort Set7BitsMask = 0b1111_1110_0000_0000; - private static readonly byte[] _lookup = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; /// @@ -63,36 +56,15 @@ public static void HandleNumbits(int numbits, byte[] bitvec, scoped ref int pc) } } - switch (numbits) + + ushort setNBitsMask = (ushort)(~((1 << 32 - numbits) - 1)); + if(numbits > 1) + { + bitvec.SetN(pc, setNBitsMask); + pc += numbits; + } else { - case 1: - bitvec.Set1(pc); - pc += 1; - break; - case 2: - bitvec.SetN(pc, Set2BitsMask); - pc += 2; - break; - case 3: - bitvec.SetN(pc, Set3BitsMask); - pc += 3; - break; - case 4: - bitvec.SetN(pc, Set4BitsMask); - pc += 4; - break; - case 5: - bitvec.SetN(pc, Set5BitsMask); - pc += 5; - break; - case 6: - bitvec.SetN(pc, Set6BitsMask); - pc += 6; - break; - case 7: - bitvec.SetN(pc, Set7BitsMask); - pc += 7; - break; + bitvec.Set1(pc); } } /// From ba97f7a839a9d5377aef5ecda154e66323d419ed Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 24 Jan 2024 21:27:53 +0100 Subject: [PATCH 019/255] jump jumpv validation --- .../Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index b5f77eacbf6..565eacf5351 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -526,7 +526,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code, short[] reachedOpc } else if (opcode is Instruction.RJUMPV) { - byte count = code[pos]; + byte maxIndex = code[pos]; - pos += ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH; + pos += ONE_BYTE_LENGTH + (maxIndex + 1) * TWO_BYTE_LENGTH; } else if (opcode is Instruction.SWAPN or Instruction.DUPN or Instruction.EXCHANGE) { @@ -823,7 +823,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } case Instruction.RJUMPV: { - var count = code[posPostInstruction]; + var count = code[posPostInstruction] + 1; immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); for (short j = 0; j < count; j++) { From d80806b2462a9ad84bbda9fcc80c19dbe8590a96 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 24 Jan 2024 23:49:49 +0100 Subject: [PATCH 020/255] update opcode values, and added metadata for *CALL2 opcodes --- .../EvmObjectFormat/EofCodeValidator.cs | 28 +++++++++++++-- src/Nethermind/Nethermind.Evm/Instruction.cs | 35 ++++++++++++------- .../Nethermind.Evm/VirtualMachine.cs | 28 +++++++-------- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 565eacf5351..b9e3591605e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -489,7 +489,6 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= header.CodeSections.Count) { if (Logger.IsTrace) Logger.Trace($"EIP-6206 : JUMPF to unknown code section"); @@ -505,6 +504,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= header.CodeSections.Count) { @@ -580,6 +579,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= header.DataSection.Size) { @@ -605,6 +604,26 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : DATALOADN Argument underflow"); + return false; + } + + ushort containerId = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); + + if (containerId >= 0 && containerId < header.ContainerSection?.Count) + { + + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : RETURNCONTRACT's immediate argument must be less than containersection.Count i.e : {header.ContainerSection?.Count}"); + return false; + } + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); } if (opcode is Instruction.CREATE3) @@ -701,6 +720,9 @@ public bool ValidateReachableCode(in ReadOnlySpan code, short[] reachedOpc else if (opcode is Instruction.DATALOADN) { pos += TWO_BYTE_LENGTH; + } else if (opcode is Instruction.RETURNCONTRACT) + { + pos += ONE_BYTE_LENGTH; } } return true; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 5c8822b39e7..5daa39ac175 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -187,20 +187,20 @@ public enum Instruction : byte CREATE3 = 0xec, CREATE4 = 0xed, RETURNCONTRACT = 0xee, - DATALOAD = 0xe8, - DATALOADN = 0xe9, - DATASIZE = 0xea, - DATACOPY = 0xeb, + DATALOAD = 0xd0, + DATALOADN = 0xd1, + DATASIZE = 0xd2, + DATACOPY = 0xd3, DUPN = 0xe6, SWAPN = 0xe7, - EXCHANGE = 0xf8, // random value opcode spec has collision + EXCHANGE = 0xe8, // random value opcode spec has collision RETURNDATALOAD = 0xf7, // opcode value not spec-ed - EOFCALL = 0xba, - EOFSTATICCALL = 0xbb, // StaticCallEnabled - EOFDELEGATECALL = 0xbc, // DelegateCallEnabled + CALL2 = 0xf8, + DELEGATECALL2 = 0xf9, // DelegateCallEnabled + STATICCALL2 = 0xfb, // StaticCallEnabled } public static class InstructionExtensions @@ -213,6 +213,9 @@ public static int GetImmediateCount(this Instruction instruction, bool IsEofCont Instruction.RJUMP or Instruction.RJUMPI => IsEofContext ? EvmObjectFormat.TWO_BYTE_LENGTH : 0, Instruction.RJUMPV => IsEofContext ? jumpvCount * EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.ONE_BYTE_LENGTH : 0, >= Instruction.PUSH0 and <= Instruction.PUSH32 => instruction - Instruction.PUSH0, + Instruction.DATALOADN => IsEofContext ? EvmObjectFormat.TWO_BYTE_LENGTH: 0, + Instruction.RETURNCONTRACT => IsEofContext ? EvmObjectFormat.ONE_BYTE_LENGTH : 0, + Instruction.CREATE3 => IsEofContext ? EvmObjectFormat.ONE_BYTE_LENGTH : 0, _ => 0 }; public static bool IsTerminating(this Instruction instruction) => instruction switch @@ -235,6 +238,10 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) 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.RETURNCONTRACT or Instruction.CREATE4 or Instruction.CREATE3 => IsEofContext, + Instruction.DATACOPY or Instruction.DATALOAD or Instruction.DATALOADN => IsEofContext, + Instruction.STATICCALL2 or Instruction.DELEGATECALL2 or Instruction.CALL2 => IsEofContext, + Instruction.RETURNDATACOPY => IsEofContext, Instruction.CALL => !IsEofContext, Instruction.CALLCODE => !IsEofContext, Instruction.DELEGATECALL => !IsEofContext, @@ -352,7 +359,11 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.SWAPN => (null, null, 1), Instruction.DUPN => (null, null, 1), Instruction.EXCHANGE => (null, null, 1), - _ => throw new NotImplementedException($"Instruction {instruction} not implemented") + + Instruction.CALL2 => (3, 1, 0), + Instruction.STATICCALL2 => (3, 1, 0), + Instruction.DELEGATECALL2 => (3, 1, 0), + _ => throw new NotImplementedException(), }; public static string? GetName(this Instruction instruction, bool isPostMerge = false, IReleaseSpec? spec = null) @@ -360,9 +371,9 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) spec ??= Frontier.Instance; return instruction switch { - Instruction.EOFCALL => "CALL" , - Instruction.EOFSTATICCALL => "STATICCALL", // StaticCallEnabled - Instruction.EOFDELEGATECALL => "DELEGATECALL", + Instruction.CALL2 => "CALL" , + Instruction.STATICCALL2 => "STATICCALL", // StaticCallEnabled + Instruction.DELEGATECALL2 => "DELEGATECALL", Instruction.PREVRANDAO when !isPostMerge => "DIFFICULTY", Instruction.RJUMP => spec.IsEofEnabled ? "RJUMP" : "BEGINSUB", Instruction.RJUMPI => spec.IsEofEnabled ? "RJUMPI" : "RETURNSUB", diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3615362bb0a..67d2e739f69 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2448,9 +2448,9 @@ private CallResult ExecuteCode Date: Fri, 26 Jan 2024 21:41:54 +0100 Subject: [PATCH 021/255] fix instruction metadata --- .../Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs | 7 ++++--- src/Nethermind/Nethermind.Evm/Instruction.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index b9e3591605e..7a81b5d0b5f 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -22,7 +22,7 @@ internal static class EvmObjectFormat struct Worklet { public Worklet(ushort position, ushort stackHeight) - { + { Position = position; StackHeight = stackHeight; } @@ -701,7 +701,6 @@ public bool ValidateReachableCode(in ReadOnlySpan code, short[] reachedOpc else if (opcode is Instruction.RJUMPV) { byte maxIndex = code[pos]; - pos += ONE_BYTE_LENGTH + (maxIndex + 1) * TWO_BYTE_LENGTH; } else if (opcode is Instruction.SWAPN or Instruction.DUPN or Instruction.EXCHANGE) @@ -720,7 +719,8 @@ public bool ValidateReachableCode(in ReadOnlySpan code, short[] reachedOpc else if (opcode is Instruction.DATALOADN) { pos += TWO_BYTE_LENGTH; - } else if (opcode is Instruction.RETURNCONTRACT) + } + else if (opcode is Instruction.RETURNCONTRACT) { pos += ONE_BYTE_LENGTH; } @@ -734,6 +734,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea short[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); + int curr_outputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; int peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 5daa39ac175..10ba5ff036e 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -351,7 +351,7 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.CREATE4 => (5, 1, 0), Instruction.RETURNCONTRACT => (2, 2, 1), Instruction.DATALOAD => (1, 1, 0), - Instruction.DATALOADN => (0, 1, 1), + Instruction.DATALOADN => (0, 1, 2), Instruction.DATASIZE => (0, 1, 0), Instruction.DATACOPY => (3, 1, 0), From 0632fceda7db30a6146819f1a164c6591c310afa Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 30 Jan 2024 17:03:55 +0100 Subject: [PATCH 022/255] fix build issues --- src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs | 2 +- .../Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs | 2 +- src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index 5e490186992..41e029fe3be 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -55,7 +55,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(ByteCode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, []), inputData: default ); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs index d3d8889bcc7..7f85937e36d 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs @@ -86,7 +86,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(_bytecode.Concat(_bytecode).Concat(_bytecode).Concat(_bytecode).ToArray()), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, []), inputData: default ); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs index 93e158881a7..fa27a64d3e9 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs @@ -97,7 +97,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(Bytecode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, []), inputData: default ); From f7be1effbdec256b9a21aecfd759535547f9aa5f Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 31 Jan 2024 16:31:26 +0100 Subject: [PATCH 023/255] change reachable code check --- .../EvmObjectFormat/EofCodeValidator.cs | 69 ++++++++++--------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 7a81b5d0b5f..a2e620932da 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -694,35 +694,31 @@ public bool ValidateReachableCode(in ReadOnlySpan code, short[] reachedOpc } pos++; - if (opcode is Instruction.RJUMP or Instruction.RJUMPI or Instruction.CALLF or Instruction.JUMPF) + switch(opcode) { - pos += TWO_BYTE_LENGTH; - } - else if (opcode is Instruction.RJUMPV) - { - byte maxIndex = code[pos]; - pos += ONE_BYTE_LENGTH + (maxIndex + 1) * TWO_BYTE_LENGTH; - } - else if (opcode is Instruction.SWAPN or Instruction.DUPN or Instruction.EXCHANGE) - { - pos += ONE_BYTE_LENGTH; - } - else if (opcode is >= Instruction.PUSH1 and <= Instruction.PUSH32) - { - int len = opcode - Instruction.PUSH0; - pos += len; - } - else if (opcode is Instruction.CREATE3) - { - pos += ONE_BYTE_LENGTH; - } - else if (opcode is Instruction.DATALOADN) - { - pos += TWO_BYTE_LENGTH; - } - else if (opcode is Instruction.RETURNCONTRACT) - { - pos += ONE_BYTE_LENGTH; + case Instruction.RJUMP or Instruction.RJUMPI or Instruction.CALLF: + pos += TWO_BYTE_LENGTH; break; + case Instruction.JUMPF: + pos += TWO_BYTE_LENGTH; break; // maybe should break looping and consider leftover code unreachable? + case Instruction.RJUMPV: + byte maxIndex = code[pos]; + pos += ONE_BYTE_LENGTH + (maxIndex + 1) * TWO_BYTE_LENGTH; + break; + case Instruction.SWAPN or Instruction.DUPN or Instruction.EXCHANGE: + pos += ONE_BYTE_LENGTH; break; + case Instruction.CREATE3: + pos += ONE_BYTE_LENGTH; break; + case Instruction.DATALOADN: + pos += TWO_BYTE_LENGTH; break; + case Instruction.RETURNCONTRACT: + pos += ONE_BYTE_LENGTH; break; + default: + if(opcode is >= Instruction.PUSH1 and <= Instruction.PUSH32) + { + int len = opcode - Instruction.PUSH0; + pos += len; + } + break; } } return true; @@ -741,6 +737,8 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea ushort worksetTop = 0; ushort worksetPointer = 0; Worklet[] workset = ArrayPool.Shared.Rent(worksetCount + 1); + int unreachedBytes = code.Length; + try { PushWorklet(workset, ref worksetTop, new Worklet(0, (ushort)peakStackHeight)); @@ -766,18 +764,20 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea else { recordedStackHeight[worklet.Position] = (short)(worklet.StackHeight + 1); + unreachedBytes -= ONE_BYTE_LENGTH; } switch (opcode) { case Instruction.CALLF or Instruction.JUMPF: - ushort sectionIndex = code.Slice(posPostInstruction, TWO_BYTE_LENGTH).ReadEthUInt16(); + ushort sectionIndex = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); inputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; outputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; outputs = (ushort)(outputs == 0x80 ? 0 : outputs); ushort maxStackHeigh = typesection.Slice(sectionIndex * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); + unreachedBytes -= immediates.Value; if (worklet.StackHeight + maxStackHeigh > MAX_STACK_HEIGHT) { @@ -789,15 +789,18 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea byte imm = code[posPostInstruction]; inputs = (ushort)(imm + 1); outputs = (ushort)(inputs + 1); + unreachedBytes -= immediates.Value; break; case Instruction.SWAPN: imm = code[posPostInstruction]; outputs = inputs = (ushort)(1 + imm); + unreachedBytes -= immediates.Value; break; case Instruction.EXCHANGE: byte imm_n = (byte)((code[posPostInstruction] >> 4) + 1); byte imm_m = (byte)((code[posPostInstruction] & 0x0F) + 1); outputs = inputs = (ushort)(imm_n + imm_m); + unreachedBytes -= immediates.Value; break; } @@ -830,18 +833,20 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } case Instruction.RJUMP: { - short offset = code.Slice(posPostInstruction, TWO_BYTE_LENGTH).ReadEthInt16(); + short offset = code.Slice(posPostInstruction, immediates.Value).ReadEthInt16(); int jumpDestination = posPostInstruction + immediates.Value + offset; PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); stop = true; + unreachedBytes -= immediates.Value; break; } case Instruction.RJUMPI: { - var offset = code.Slice(posPostInstruction, TWO_BYTE_LENGTH).ReadEthInt16(); + var offset = code.Slice(posPostInstruction, immediates.Value).ReadEthInt16(); var jumpDestination = posPostInstruction + immediates + offset; PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); posPostInstruction += immediates.Value; + unreachedBytes -= immediates.Value; break; } case Instruction.RJUMPV: @@ -856,10 +861,12 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); } posPostInstruction += immediates.Value; + unreachedBytes -= immediates.Value; break; } default: { + unreachedBytes -= immediates.Value; posPostInstruction += immediates.Value; break; } From da924b61cfc2ddb4014cfa03ab733d4adee222ef Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 31 Jan 2024 16:31:55 +0100 Subject: [PATCH 024/255] remove old check and use new one --- .../EvmObjectFormat/EofCodeValidator.cs | 43 +------------------ 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index a2e620932da..c23fc36302d 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -682,47 +682,6 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan.Shared.Return(jumpdests, true); } } - public bool ValidateReachableCode(in ReadOnlySpan code, short[] reachedOpcode) - { - for (int pos = 0; pos < code.Length;) - { - var opcode = (Instruction)code[pos]; - - if (reachedOpcode[pos] == 0) - { - return false; - } - - pos++; - switch(opcode) - { - case Instruction.RJUMP or Instruction.RJUMPI or Instruction.CALLF: - pos += TWO_BYTE_LENGTH; break; - case Instruction.JUMPF: - pos += TWO_BYTE_LENGTH; break; // maybe should break looping and consider leftover code unreachable? - case Instruction.RJUMPV: - byte maxIndex = code[pos]; - pos += ONE_BYTE_LENGTH + (maxIndex + 1) * TWO_BYTE_LENGTH; - break; - case Instruction.SWAPN or Instruction.DUPN or Instruction.EXCHANGE: - pos += ONE_BYTE_LENGTH; break; - case Instruction.CREATE3: - pos += ONE_BYTE_LENGTH; break; - case Instruction.DATALOADN: - pos += TWO_BYTE_LENGTH; break; - case Instruction.RETURNCONTRACT: - pos += ONE_BYTE_LENGTH; break; - default: - if(opcode is >= Instruction.PUSH1 and <= Instruction.PUSH32) - { - int len = opcode - Instruction.PUSH0; - pos += len; - } - break; - } - } - return true; - } public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection, ushort worksetCount) { static Worklet PopWorklet(Worklet[] workset, ref ushort worksetPointer) => workset[worksetPointer++]; @@ -894,7 +853,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } } - if (!ValidateReachableCode(code, recordedStackHeight)) + if (unreachedBytes > 0) { if (Logger.IsTrace) Logger.Trace($"EIP-5450 : bytecode has unreachable segments"); return false; From 4929e1aa04f8bb6db88829d956f5947f2b9e1c6e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 31 Jan 2024 16:40:34 +0100 Subject: [PATCH 025/255] changing RJUMP check and considring it Terminating opcode --- .../Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs | 5 +---- src/Nethermind/Nethermind.Evm/Instruction.cs | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index c23fc36302d..88749a270e2 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -704,9 +704,8 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea while (worksetPointer < worksetTop) { Worklet worklet = PopWorklet(workset, ref worksetPointer); - bool stop = false; - while (!stop) + while (worklet.Position < code.Length) { Instruction opcode = (Instruction)code[worklet.Position]; (ushort? inputs, ushort? outputs, ushort? immediates) = opcode.StackRequirements(); @@ -795,7 +794,6 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea short offset = code.Slice(posPostInstruction, immediates.Value).ReadEthInt16(); int jumpDestination = posPostInstruction + immediates.Value + offset; PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); - stop = true; unreachedBytes -= immediates.Value; break; } @@ -832,7 +830,6 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } worklet.Position = posPostInstruction; - if (stop) break; if (opcode.IsTerminating()) { diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 10ba5ff036e..7f7201b7934 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -222,6 +222,7 @@ public static int GetImmediateCount(this Instruction instruction, bool IsEofCont { Instruction.RETF or Instruction.INVALID or Instruction.STOP or Instruction.RETURN or Instruction.REVERT => true, Instruction.JUMPF or Instruction.RETURNCONTRACT => true, + Instruction.RJUMP => true, // Instruction.SELFDESTRUCT => true _ => false }; From 3f507a989a823277e19b02d61cb942402f737ca9 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 1 Feb 2024 01:04:03 +0100 Subject: [PATCH 026/255] minor refactors --- .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 29 ++++++++++++++++++- .../EvmObjectFormat/EofCodeValidator.cs | 23 +++++++-------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 66683a11d0e..50a3be0d0ca 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -112,6 +112,32 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase } ).ToArray(); + private static readonly Instruction[] PragueInstructions = + ShanghaiInstructions.Union( + new Instruction[] + { + Instruction.RJUMP, + Instruction.RJUMPI, + Instruction.RJUMPV, + Instruction.CALLF, + Instruction.RETF, + Instruction.JUMPF, + Instruction.CREATE3, + Instruction.CREATE4, + Instruction.RETURNCONTRACT, + Instruction.DATASIZE, + Instruction.DATACOPY, + Instruction.DATALOAD, + Instruction.DATALOADN, + Instruction.SWAPN, + Instruction.DUPN, + Instruction.EXCHANGE, + Instruction.CALL2, + Instruction.DELEGATECALL2, + Instruction.STATICCALL2, + } + ).ToArray(); + private readonly Dictionary _validOpcodes = new() { @@ -127,7 +153,8 @@ private readonly Dictionary _validOpcodes {(ForkActivation)MainnetSpecProvider.LondonBlockNumber, LondonInstructions}, {MainnetSpecProvider.ShanghaiActivation, ShanghaiInstructions}, {MainnetSpecProvider.CancunActivation, CancunInstructions}, - {(long.MaxValue, ulong.MaxValue), CancunInstructions} + {MainnetSpecProvider.PragueActivation, PragueInstructions}, + {(long.MaxValue, ulong.MaxValue), PragueInstructions} }; private const string InvalidOpCodeErrorMessage = "BadInstruction"; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 88749a270e2..aaea32308b9 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -689,9 +689,9 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea short[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); - - int curr_outputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - int peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + + ushort curr_outputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + ushort peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; ushort worksetTop = 0; ushort worksetPointer = 0; Worklet[] workset = ArrayPool.Shared.Rent(worksetCount + 1); @@ -700,7 +700,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea try { - PushWorklet(workset, ref worksetTop, new Worklet(0, (ushort)peakStackHeight)); + PushWorklet(workset, ref worksetTop, new Worklet(0, peakStackHeight)); while (worksetPointer < worksetTop) { Worklet worklet = PopWorklet(workset, ref worksetPointer); @@ -789,19 +789,11 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea break; } - case Instruction.RJUMP: + case Instruction.RJUMP or Instruction.RJUMPI: { short offset = code.Slice(posPostInstruction, immediates.Value).ReadEthInt16(); int jumpDestination = posPostInstruction + immediates.Value + offset; PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); - unreachedBytes -= immediates.Value; - break; - } - case Instruction.RJUMPI: - { - var offset = code.Slice(posPostInstruction, immediates.Value).ReadEthInt16(); - var jumpDestination = posPostInstruction + immediates + offset; - PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); posPostInstruction += immediates.Value; unreachedBytes -= immediates.Value; break; @@ -833,6 +825,11 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (opcode.IsTerminating()) { + if(opcode is Instruction.RJUMP) + { + break; + } + var expectedHeight = opcode is Instruction.RETF ? typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] : worklet.StackHeight; if (expectedHeight != worklet.StackHeight) { From 5e8b52818c54bc1847215672ddfbacef658942b9 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 1 Feb 2024 15:21:41 +0100 Subject: [PATCH 027/255] add pragueActivation and fix releaseSpec --- src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 7 +++++-- src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs | 2 +- src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs | 5 +++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 50a3be0d0ca..b2c48eb9012 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -113,7 +113,7 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase ).ToArray(); private static readonly Instruction[] PragueInstructions = - ShanghaiInstructions.Union( + CancunInstructions.Union( new Instruction[] { Instruction.RJUMP, diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index dc7f45db291..32b3df9fc70 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2075,9 +2075,12 @@ private CallResult ExecuteCode 0)) + { + goto InvalidInstruction; + } + Metrics.Creates++; if (vmState.IsStatic) goto StaticCallViolation; (exceptionType, returnData) = InstructionEofCreate(vmState, ref codeSection, ref stack, ref gasAvailable, spec, instruction); diff --git a/src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs b/src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs index 88dc67646e4..eef21795c65 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/17_Prague.cs @@ -7,7 +7,7 @@ namespace Nethermind.Specs.Forks; -public class Prague : Shanghai +public class Prague : Cancun { private static IReleaseSpec _instance; diff --git a/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs b/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs index 1670bde6c46..f5528be4599 100644 --- a/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs @@ -45,7 +45,8 @@ public IReleaseSpec GetSpec(ForkActivation forkActivation) => { BlockNumber: < GrayGlacierBlockNumber } => ArrowGlacier.Instance, { Timestamp: null } or { Timestamp: < ShanghaiBlockTimestamp } => GrayGlacier.Instance, { Timestamp: < CancunBlockTimestamp } => Shanghai.Instance, - _ => Cancun.Instance + { Timestamp: < PragueBlockTimestamp } => Cancun.Instance, + _ => Prague.Instance }; public void UpdateMergeTransitionInfo(long? blockNumber, UInt256? terminalTotalDifficulty = null) @@ -84,7 +85,7 @@ public void UpdateMergeTransitionInfo(long? blockNumber, UInt256? terminalTotalD (ForkActivation)GrayGlacierBlockNumber, ShanghaiActivation, CancunActivation, - //PragueActivation, + PragueActivation, //OsakaActivation }; From 6ce44cafa96f4a47482416347c98b41afbf4a7ad Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 2 Feb 2024 19:00:41 +0100 Subject: [PATCH 028/255] fixes to header parsing and validation --- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 12 +- .../EvmObjectFormat/EofCodeInfo.cs | 3 +- .../EvmObjectFormat/EofCodeValidator.cs | 70 +++++------ src/Nethermind/Nethermind.Evm/EvmStack.cs | 4 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 18 ++- .../Nethermind.Evm/VirtualMachine.cs | 111 +++++++++++------- 6 files changed, 124 insertions(+), 94 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index fcee5f18ae5..51463a51045 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -23,14 +23,14 @@ public static byte[] CreateCodeBitmap(ReadOnlySpan code, bool isEof = fals for (int pc = 0; pc < code.Length;) { - Instruction op = (Instruction)code[pc]; + var opMetadaata = ((Instruction)code[pc]).StackRequirements(); + pc++; - int numbits = op switch - { - Instruction.RJUMPV => isEof ? op.GetImmediateCount(isEof, code[pc]) : 0, - _ => op.GetImmediateCount(isEof), - }; + int numbits = + code[pc] == (byte)Instruction.RJUMPV + ? Instruction.RJUMPV.GetImmediateCount(isEof, code[pc]) + : opMetadaata.immediates.Value; if (numbits == 0) continue; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index d37c4c7ce47..df52e7b5a2e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -11,10 +11,11 @@ namespace Nethermind.Evm.CodeAnalysis; public class EofCodeInfo : ICodeInfo { private readonly CodeInfo _codeInfo; + private readonly EofHeader _header; public ReadOnlyMemory MachineCode => _codeInfo.MachineCode; public IPrecompile? Precompile => _codeInfo.Precompile; - public byte Version => _header.Version; + public int Version => _header.Version; public ReadOnlyMemory TypeSection { get; } public ReadOnlyMemory CodeSection { get; } public ReadOnlyMemory DataSection { get; } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index aaea32308b9..babda138185 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -96,7 +96,6 @@ public static bool IsValidEof(ReadOnlySpan container, bool validateSubCont } return true; } - } header = null; @@ -133,8 +132,8 @@ internal enum Separator : byte { KIND_TYPE = 0x01, KIND_CODE = 0x02, - KIND_DATA = 0x03, - KIND_CONTAINER = 0x04, + KIND_CONTAINER = 0x03, + KIND_DATA = 0x04, TERMINATOR = 0x00 } @@ -153,6 +152,7 @@ internal enum Separator : byte 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; @@ -198,7 +198,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => Sizes sectionSizes = new(); int TYPESECTION_HEADER_STARTOFFSET = VERSION_OFFSET + ONE_BYTE_LENGTH; - int TYPESECTION_HEADER_ENDOFFSET = VERSION_OFFSET + ONE_BYTE_LENGTH + TWO_BYTE_LENGTH; + int TYPESECTION_HEADER_ENDOFFSET = TYPESECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; if (container[TYPESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_TYPE) { if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Code is not Eof version {VERSION}"); @@ -212,7 +212,6 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => } int CODESECTION_HEADER_STARTOFFSET = TYPESECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; - int CODESECTION_HEADER_ENDOFFSET = CODESECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; if (container[CODESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_CODE) { if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Eof{VERSION}, Code header is not well formatted"); @@ -223,7 +222,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => sectionSizes.CodeSectionSize = (ushort)(numberOfCodeSections * TWO_BYTE_LENGTH); if (numberOfCodeSections > MAXIMUM_NUM_CODE_SECTIONS) { - if (Logger.IsTrace) Logger.Trace($"EIP-3540 : code sections count must not exceed 1024"); + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : code sections count must not exceed {MAXIMUM_NUM_CODE_SECTIONS}"); return false; } @@ -242,31 +241,37 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => codeSections[pos] = codeSectionSize; } - CODESECTION_HEADER_ENDOFFSET = CODESECTION_HEADER_PREFIX_SIZE + numberOfCodeSections * TWO_BYTE_LENGTH; + var CODESECTION_HEADER_ENDOFFSET = CODESECTION_HEADER_PREFIX_SIZE + numberOfCodeSections * TWO_BYTE_LENGTH; int CONTAINERSECTION_HEADER_STARTOFFSET = CODESECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; int? CONTAINERSECTION_HEADER_ENDOFFSET = null; - int[] containersSections = null; + int[] containerSections = null; if (container[CONTAINERSECTION_HEADER_STARTOFFSET] == (byte)Separator.KIND_CONTAINER) { - int numberOfContainersSections = GetUInt16(container, CONTAINERSECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); - sectionSizes.ContainerSectionSize = (ushort)(numberOfContainersSections * TWO_BYTE_LENGTH); + ushort numberOfContainerSections = GetUInt16(container, CONTAINERSECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); + sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * TWO_BYTE_LENGTH); + if (numberOfContainerSections > MAXIMUM_NUM_CONTAINER_SECTIONS) + { + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); + return false; + } - containersSections = new int[numberOfContainersSections]; - for (ushort pos = 0; pos < numberOfCodeSections; pos++) + containerSections = new int[numberOfContainerSections]; + int CONTAINER_SECTION_HEADER_PREFIX_SIZE = CONTAINERSECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; + for (ushort pos = 0; pos < numberOfContainerSections; pos++) { - int currentCodeSizeOffset = CODESECTION_HEADER_STARTOFFSET + pos * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size - int containerSectionSize = GetUInt16(container, currentCodeSizeOffset + ONE_BYTE_LENGTH); + int currentContainerSizeOffset = CONTAINER_SECTION_HEADER_PREFIX_SIZE + pos * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size + int containerSectionSize = GetUInt16(container, currentContainerSizeOffset + ONE_BYTE_LENGTH); if (containerSectionSize == 0) { - if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Empty Code Section are not allowed, CodeSectionSize must be > 0 but found {containerSectionSize}"); + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Empty Code Section are not allowed, containerSectionSize must be > 0 but found {containerSectionSize}"); return false; } - containersSections[pos] = containerSectionSize; - CONTAINERSECTION_HEADER_ENDOFFSET = currentCodeSizeOffset + containerSectionSize; + containerSections[pos] = containerSectionSize; } + CONTAINERSECTION_HEADER_ENDOFFSET = CONTAINER_SECTION_HEADER_PREFIX_SIZE + numberOfContainerSections * TWO_BYTE_LENGTH; } @@ -294,18 +299,18 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => ); CompoundSectionHeader codeSectionHeader = new( - Start: typeSectionHeader.Start + typeSectionHeader.Size, + Start: typeSectionHeader.EndOffset, SubSectionsSizes: codeSections ); - CompoundSectionHeader? containerSectionHeader = containersSections is null ? null + CompoundSectionHeader? containerSectionHeader = containerSections is null ? null : new( - Start: codeSectionHeader.Start + codeSectionHeader.Size, - SubSectionsSizes: containersSections + Start: codeSectionHeader.EndOffset, + SubSectionsSizes: containerSections ); SectionHeader dataSectionHeader = new( - Start: containerSectionHeader?.Start + containerSectionHeader?.Size ?? codeSectionHeader.EndOffset, + Start: containerSectionHeader?.EndOffset ?? codeSectionHeader.EndOffset, Size: sectionSizes.DataSectionSize ); @@ -324,10 +329,11 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => public bool ValidateBody(ReadOnlySpan container, EofHeader header) { int startOffset = header.TypeSection.Start; - int calculatedCodeLength = header.TypeSection.Size - + header.CodeSections.Size - + header.DataSection.Size - + (header.ContainerSection?.Size ?? 0); + int calculatedCodeLength = + header.TypeSection.Size + + header.CodeSections.Size + + header.DataSection.Size + + (header.ContainerSection?.Size ?? 0); CompoundSectionHeader codeSections = header.CodeSections; ReadOnlySpan contractBody = container[startOffset..]; (int typeSectionStart, ushort typeSectionSize) = header.TypeSection; @@ -381,24 +387,23 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header) bool isNonReturning = typesection[sectionIdx * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; - ReadOnlySpan code = container.Slice(codeSectionStartOffset, codeSectionSize); + ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSectionStartOffset, codeSectionSize); if (!ValidateInstructions(sectionIdx, isNonReturning, typesection, code, header, validationQueue, out ushort jumpsCount)) { - ArrayPool.Shared.Return(visitedSections); + ArrayPool.Shared.Return(visitedSections, true); return false; } } var HasNoNonReachableCodeSections = visitedSections[..header.CodeSections.Count].All(id => id); - ArrayPool.Shared.Return(visitedSections); + ArrayPool.Shared.Return(visitedSections, true); return HasNoNonReachableCodeSections; - } bool ValidateTypeSection(ReadOnlySpan types) { - if (types[INPUTS_OFFSET] != 0 || types[OUTPUTS_OFFSET] != 0) + if (types[INPUTS_OFFSET] != 0 || types[OUTPUTS_OFFSET] != NON_RETURNING) { if (Logger.IsTrace) Logger.Trace($"EIP-4750: first 2 bytes of type section must be 0s"); return false; @@ -422,7 +427,7 @@ bool ValidateTypeSection(ReadOnlySpan types) return false; } - if (outputCount > OUTPUTS_MAX) + if (outputCount > OUTPUTS_MAX && outputCount != NON_RETURNING) { if (Logger.IsTrace) Logger.Trace("EIP-3540 : Too many outputs"); return false; @@ -639,7 +644,6 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= header.ContainerSection?.Count) { - if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : CREATE3's immediate must falls within the Containers' range available, i.e : {header.CodeSections.Count}"); return false; } diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 0befd085586..0a0223e514d 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -415,8 +415,8 @@ public readonly bool Exchange(int n, int m) ref byte bytes = ref MemoryMarshal.GetReference(_bytes); - ref byte bottom = ref Unsafe.Add(ref bytes, (n - m) * WordSize); - ref byte top = ref Unsafe.Add(ref bytes, (n - 1) * WordSize); + ref byte bottom = ref Unsafe.Add(ref bytes, (Head - n - m) * WordSize); + ref byte top = ref Unsafe.Add(ref bytes, (Head - n) * WordSize); Word buffer = Unsafe.ReadUnaligned(ref bottom); Unsafe.WriteUnaligned(ref bottom, Unsafe.ReadUnaligned(ref top)); diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 7f7201b7934..5e6e2994047 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -206,17 +206,12 @@ public enum Instruction : byte public static class InstructionExtensions { public static int GetImmediateCount(this Instruction instruction, bool IsEofContext, byte jumpvCount = 0) - => instruction switch + => + instruction switch { - Instruction.CALLF or Instruction.JUMPF => IsEofContext ? EvmObjectFormat.TWO_BYTE_LENGTH : 0, - Instruction.DUPN or Instruction.SWAPN => IsEofContext ? EvmObjectFormat.ONE_BYTE_LENGTH : 0, - Instruction.RJUMP or Instruction.RJUMPI => IsEofContext ? EvmObjectFormat.TWO_BYTE_LENGTH : 0, Instruction.RJUMPV => IsEofContext ? jumpvCount * EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.ONE_BYTE_LENGTH : 0, >= Instruction.PUSH0 and <= Instruction.PUSH32 => instruction - Instruction.PUSH0, - Instruction.DATALOADN => IsEofContext ? EvmObjectFormat.TWO_BYTE_LENGTH: 0, - Instruction.RETURNCONTRACT => IsEofContext ? EvmObjectFormat.ONE_BYTE_LENGTH : 0, - Instruction.CREATE3 => IsEofContext ? EvmObjectFormat.ONE_BYTE_LENGTH : 0, - _ => 0 + _ => IsEofContext ? instruction.StackRequirements().immediates.Value : 0 }; public static bool IsTerminating(this Instruction instruction) => instruction switch { @@ -240,9 +235,9 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE => IsEofContext, Instruction.RJUMP or Instruction.RJUMPI or Instruction.RJUMPV => IsEofContext, Instruction.RETURNCONTRACT or Instruction.CREATE4 or Instruction.CREATE3 => IsEofContext, - Instruction.DATACOPY or Instruction.DATALOAD or Instruction.DATALOADN => IsEofContext, + Instruction.DATACOPY or Instruction.DATASIZE or Instruction.DATALOAD or Instruction.DATALOADN => IsEofContext, Instruction.STATICCALL2 or Instruction.DELEGATECALL2 or Instruction.CALL2 => IsEofContext, - Instruction.RETURNDATACOPY => IsEofContext, + Instruction.RETURNDATALOAD => IsEofContext, Instruction.CALL => !IsEofContext, Instruction.CALLCODE => !IsEofContext, Instruction.DELEGATECALL => !IsEofContext, @@ -306,6 +301,7 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.EXTCODECOPY => (4, 0, 0), Instruction.RETURNDATASIZE => (0, 1, 0), Instruction.RETURNDATACOPY => (3, 0, 0), + Instruction.RETURNDATALOAD => (1, 1, 0), Instruction.EXTCODEHASH => (1, 1, 0), Instruction.BLOCKHASH => (1, 1, 0), Instruction.COINBASE => (0, 1, 0), @@ -364,7 +360,7 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.CALL2 => (3, 1, 0), Instruction.STATICCALL2 => (3, 1, 0), Instruction.DELEGATECALL2 => (3, 1, 0), - _ => throw new NotImplementedException(), + _ => throw new NotImplementedException($"opcode {instruction} not implemented yet"), }; public static string? GetName(this Instruction instruction, bool isPostMerge = false, IReleaseSpec? spec = null) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 32b3df9fc70..2de924f9c41 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -48,7 +48,7 @@ namespace Nethermind.Evm; public class VirtualMachine : IVirtualMachine { - public const int MaxCallDepth = 1024; + public const int MaxCallDepth = EvmObjectFormat.Eof1.RETURN_STACK_MAX_HEIGHT; internal static FrozenDictionary PrecompileCode { get; } = InitializePrecompiledContracts(); internal static LruCache CodeCache { get; } = new(MemoryAllowance.CodeCacheSize, MemoryAllowance.CodeCacheSize, "VM bytecodes"); @@ -1598,47 +1598,48 @@ private CallResult ExecuteCode _returnDataBuffer.Length) - { - goto AccessViolation; - } + if (!spec.ReturnDataOpcodesEnabled) goto InvalidInstruction; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, 32)) goto OutOfGas; + if (!stack.PopUInt256(out a)) goto StackUnderflow; + if (!stack.PopUInt256(out b)) goto StackUnderflow; + if (!stack.PopUInt256(out result)) goto StackUnderflow; + gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result); - slice = _returnDataBuffer.Span.SliceWithZeroPadding(a, 32); - stack.PushBytes(slice); - } - else + if (UInt256.AddOverflow(result, b, out c) || c > _returnDataBuffer.Length) { - if (!spec.ReturnDataOpcodesEnabled) goto InvalidInstruction; + goto AccessViolation; + } - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out result)) goto StackUnderflow; - gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result); + if (!result.IsZero) + { + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, result)) goto OutOfGas; - if (UInt256.AddOverflow(result, b, out c) || c > _returnDataBuffer.Length) + slice = _returnDataBuffer.Span.SliceWithZeroPadding(b, (int)result); + vmState.Memory.Save(in a, in slice); + if (typeof(TTracingInstructions) == typeof(IsTracing)) { - goto AccessViolation; + _txTracer.ReportMemoryChange((long)a, in slice); } + } + break; + } + case Instruction.RETURNDATALOAD: + { + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) + goto InvalidInstruction; - if (!result.IsZero) - { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, result)) goto OutOfGas; + gasAvailable -= GasCostOf.VeryLow; - slice = _returnDataBuffer.Span.SliceWithZeroPadding(b, (int)result); - vmState.Memory.Save(in a, in slice); - if (typeof(TTracingInstructions) == typeof(IsTracing)) - { - _txTracer.ReportMemoryChange((long)a, in slice); - } - } + if (!stack.PopUInt256(out a)) goto StackUnderflow; + if (UInt256.AddOverflow(a, 32, out c) || c > _returnDataBuffer.Length) + { + goto AccessViolation; } + + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, 32)) goto OutOfGas; + + slice = _returnDataBuffer.Span.SliceWithZeroPadding(a, 32); + stack.PushBytes(slice); break; } case Instruction.BLOCKHASH: @@ -2024,7 +2025,7 @@ private CallResult ExecuteCode> 0x04 + 1; - int m = (int)codeSection[programCounter] & 0x0f + 1; + int n = (int)codeSection[programCounter] >> 0x04; + int m = (int)codeSection[programCounter] & 0x0f; stack.Exchange(n, m); @@ -2359,7 +2360,7 @@ private CallResult ExecuteCode 0)) + if (spec.IsEofEnabled && env.CodeInfo.Version > 0) { if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) goto OutOfGas; short offset = codeSection.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); @@ -2370,7 +2371,7 @@ private CallResult ExecuteCode 0)) + if (spec.IsEofEnabled && env.CodeInfo.Version > 0) { if (!UpdateGas(GasCostOf.RJumpi, ref gasAvailable)) goto OutOfGas; Span condition = stack.PopWord256(); @@ -2380,12 +2381,13 @@ private CallResult ExecuteCode 0)) + if (spec.IsEofEnabled && env.CodeInfo.Version > 0) { if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; var caseV = stack.PopByte(); @@ -2399,6 +2401,7 @@ private CallResult ExecuteCode 0)) + { + goto InvalidInstruction; + } + + if (!UpdateGas(GasCostOf.Jumpf, ref gasAvailable)) goto OutOfGas; + var index = (int)codeSection.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthUInt16(); + (int inputCount, _, int maxStackHeight) = env.CodeInfo.GetSectionMetadata(index); + + if (maxStackHeight + stack.Head > EvmObjectFormat.Eof1.MAX_STACK_HEIGHT) + { + goto StackOverflow; + } + + if (vmState.ReturnStackHead + 1 == EvmObjectFormat.Eof1.RETURN_STACK_MAX_HEIGHT) + goto InvalidSubroutineEntry; + + stack.EnsureDepth(inputCount); + sectionIndex = index; + (programCounter, _) = env.CodeInfo.SectionOffset(index); + break; + } case Instruction.RETF: { if (!spec.IsEofEnabled || !(env.CodeInfo.Version > 0)) @@ -2443,7 +2472,7 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); + exceptionType = InstructionEofCall(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; if (returnData is null) From aed51dea6919e0d482b22ffc01d12ae78b2e3ae8 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 4 Feb 2024 18:44:39 +0100 Subject: [PATCH 029/255] more fixes - stack validation of JUMPF and EXCHANGE - call to EofCreate - ICodeInfo implemetation in EofCodeInfo --- .../Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs | 2 +- .../EvmObjectFormat/EofCodeValidator.cs | 15 +++++++-------- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 7 +++++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index df52e7b5a2e..b006ccd247f 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -19,7 +19,7 @@ public class EofCodeInfo : ICodeInfo public ReadOnlyMemory TypeSection { get; } public ReadOnlyMemory CodeSection { get; } public ReadOnlyMemory DataSection { get; } - public ReadOnlyMemory? ContainerSection { get; } + public ReadOnlyMemory ContainerSection { get; } public SectionHeader SectionOffset(int sectionId) => _header.CodeSections[sectionId]; public SectionHeader ContainerOffset(int containerId) => _header.ContainerSection is null diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index babda138185..7d80fc45f9c 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -620,8 +620,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= 0 && containerId < header.ContainerSection?.Count) { @@ -639,7 +638,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= header.ContainerSection?.Count) @@ -694,7 +693,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea short[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); - ushort curr_outputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + ushort curr_outputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; ushort peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; ushort worksetTop = 0; ushort worksetPointer = 0; @@ -738,10 +737,10 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea outputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; outputs = (ushort)(outputs == 0x80 ? 0 : outputs); - ushort maxStackHeigh = typesection.Slice(sectionIndex * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); + ushort maxStackHeight = typesection.Slice(sectionIndex * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); unreachedBytes -= immediates.Value; - if (worklet.StackHeight + maxStackHeigh > MAX_STACK_HEIGHT) + if (worklet.StackHeight + maxStackHeight - inputs > MAX_STACK_HEIGHT) { if (Logger.IsTrace) Logger.Trace($"EIP-5450 : stack head during callf must not exceed {MAX_STACK_HEIGHT}"); return false; @@ -759,8 +758,8 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea unreachedBytes -= immediates.Value; break; case Instruction.EXCHANGE: - byte imm_n = (byte)((code[posPostInstruction] >> 4) + 1); - byte imm_m = (byte)((code[posPostInstruction] & 0x0F) + 1); + byte imm_n = (byte)(code[posPostInstruction] >> 4); + byte imm_m = (byte)(code[posPostInstruction] & 0x0F); outputs = inputs = (ushort)(imm_n + imm_m); unreachedBytes -= immediates.Value; break; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 2de924f9c41..8cf1f9cd221 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2084,6 +2084,7 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; @@ -3091,13 +3094,13 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref { int initCodeIdx = codeSection[vmState.ProgramCounter]; SectionHeader containerSection = env.CodeInfo.ContainerOffset(initCodeIdx); - initCode = env.CodeInfo.ContainerSection[containerSection.Start..containerSection.Size]; + initCode = env.CodeInfo.ContainerSection[containerSection.Start..containerSection.EndOffset]; break; } case Instruction.CREATE4 : { byte[] initContainerHash = stack.PopWord256().ToArray(); - var initcode = env.TxExecutionContext.InitCodes.First( + var initcode = env.TxExecutionContext.InitCodes?.First( initcode => initContainerHash == Keccak.Compute(initcode).Bytes ); if(initcode is null) From ea1075be87fd4138dac4c82aff92f5ac997137e7 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 4 Feb 2024 19:42:39 +0100 Subject: [PATCH 030/255] added some tests for eof Opcodes --- .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 105 +++++++++++++++++- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index b2c48eb9012..41de9289432 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; @@ -186,14 +187,108 @@ public void Test(long blockNumber, ulong? timestamp = null) Instruction[] validOpcodes = _validOpcodes[(blockNumber, timestamp)]; for (int i = 0; i <= byte.MaxValue; i++) { - logger.Info($"============ Testing opcode {i}=================="); + bool isEofContext = timestamp >= MainnetSpecProvider.PragueActivation.Timestamp; + bool isValidOpcode = false; + + Instruction opcode = (Instruction)i; + + if (opcode == Instruction.RETF) continue; + byte[] code = Prepare.EvmCode - .Op((byte)i) - .Done; + .Op((byte)i) + .Done; ; - bool isValidOpcode = ((Instruction)i != Instruction.INVALID) && validOpcodes.Contains((Instruction)i); - TestAllTracerWithOutput result = Execute((blockNumber, timestamp ?? 0), 1_000_000, code); + if(InstructionExtensions.IsValid(opcode, true) && !InstructionExtensions.IsValid(opcode, false)) + { + var opcodeMetadata = InstructionExtensions.StackRequirements(opcode); + opcodeMetadata.InputCount ??= 1; + opcodeMetadata.OutputCount ??= (opcode is Instruction.DUPN ? (ushort)2 : (ushort)1); + + bool isFunCall = opcode is Instruction.CALLF; + + byte[] stackHeighExpected = BitConverter.GetBytes(Math.Max(opcodeMetadata.InputCount.Value, opcodeMetadata.OutputCount.Value)); + + List codesection = new(); + + for(var j = 0; j < opcodeMetadata.InputCount; j++) + { + codesection.AddRange( + Prepare.EvmCode + .PushSingle(0) + .Done + ); + } + codesection.Add((byte)i); + + for (var j = 0; j < (opcodeMetadata.immediates ?? 3); j++) + { + if(isFunCall && j == 1) + { + codesection.Add(1); + continue; + } + codesection.Add(0); + } + + for (var j = 0; j < opcodeMetadata.OutputCount; j++) + { + codesection.AddRange( + Prepare.EvmCode + .Op(Instruction.POP) + .Done + ); + } + + if(opcode is not Instruction.JUMPF) + { + codesection.Add((byte)Instruction.STOP); + } + + + byte[] codeSectionSize = BitConverter.GetBytes((ushort)(codesection.Count)); + code = [ + // start header + 0xef, 0x00, + 0x01, + 0x01, + 0x00, (isFunCall ? (byte)0x08 : (byte)0x04), + 0x02, + 0x00, (isFunCall ? (byte)0x02 : (byte)0x01), + codeSectionSize[1], codeSectionSize[0], + .. (isFunCall ? [0x00, 0x01] : Array.Empty()), + 0x03, + 0x00, 0x01, + 0x00, 0x02, + 0x04, + 0x00, 0x20, + 0x00, + // end header + // start typesection + 0x00, 0x80, + stackHeighExpected[1], stackHeighExpected[0], + .. (isFunCall ? [0x00, 0x00, 0x00, 0x00] : Array.Empty()), + // end typesection + // start codesection + // start codesection 0 + .. codesection, + // end codesection 0 + // start codesection 1 + .. (isFunCall ? [(byte)Instruction.RETF]: Array.Empty()), + // end codesection 1 + // end codesection + // start container section + (byte)Instruction.RETURNCONTRACT, 0x00, + // end container section + // start data section + .. Enumerable.Range(0, 32).Select(b => (byte)b).ToArray() + // end data section + ]; + } + + logger.Info($"============ Testing opcode {i}=================="); + isValidOpcode = (opcode != Instruction.INVALID) && validOpcodes.Contains((Instruction)i); + TestAllTracerWithOutput result = Execute((blockNumber, timestamp ?? 0), 1_000_000, code); if (isValidOpcode) { result.Error.Should().NotBe(InvalidOpCodeErrorMessage, ((Instruction)i).ToString()); From 0c030dbac3d8b21df17250c560ff5438cbac4751 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 5 Feb 2024 22:09:30 +0100 Subject: [PATCH 031/255] added deploy_code validation (maybe redundant) --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8cf1f9cd221..505bfc754fa 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -450,10 +450,10 @@ public TransactionSubstate Run(EvmState state, IWorldState worl ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; (int start, int size) = previousState.Env.CodeInfo.ContainerOffset(containerIndex); ReadOnlySpan container = previousState.Env.CodeInfo.ContainerSection.Slice(start, size).Span; - bool invalidCode = !EvmObjectFormat.TryExtractHeader(container, out EofHeader? header); + bool isEof_unvalidated = !EvmObjectFormat.TryExtractHeader(container, out EofHeader? header); byte[] bytecodeResultArray = null; - if (!invalidCode) + if (!isEof_unvalidated) { Span bytecodeResult = new byte[container.Length + header.Value.DataSection.Size]; @@ -514,6 +514,8 @@ public TransactionSubstate Run(EvmState state, IWorldState worl bytecodeResultArray = bytecodeResult.ToArray(); } + bool invalidCode = !EvmObjectFormat.IsValidEof(bytecodeResultArray, false, out _) + && bytecodeResultArray.Length < spec.MaxCodeSize; long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray.Length, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { From 230931b21c1503ca4860e34b2ded3df750506a26 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 6 Feb 2024 00:51:14 +0100 Subject: [PATCH 032/255] - extend EOF validation rules - extend intrinsic gas calculation for eof --- .../Validators/TxValidator.cs | 13 +++++-- src/Nethermind/Nethermind.Core/Transaction.cs | 1 + .../Nethermind.Evm/IntrinsicGasCalculator.cs | 35 +++++++++++++++---- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index f1b42c056be..e5a2c9c6e77 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -7,6 +7,7 @@ using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Evm; +using Nethermind.Evm.EOF; using Nethermind.Int256; using Nethermind.TxPool; @@ -41,7 +42,7 @@ public bool IsWellFormed(Transaction transaction, IReleaseSpec releaseSpec) Validate1559GasFields(transaction, releaseSpec) && Validate3860Rules(transaction, releaseSpec) && Validate4844Fields(transaction) && - ValidateMegaEofInitcodes(transaction, releaseSpec); + ValidateMegaEofRules(transaction, releaseSpec); } private static bool Validate3860Rules(Transaction transaction, IReleaseSpec releaseSpec) => @@ -107,10 +108,18 @@ private bool ValidateSignature(Transaction tx, IReleaseSpec spec) return !spec.ValidateChainId; } - private static bool ValidateMegaEofInitcodes(Transaction transaction, IReleaseSpec spec) + private static bool ValidateMegaEofRules(Transaction transaction, IReleaseSpec spec) { if (!spec.IsEofEnabled) return true; + if (transaction.To is null && + transaction.Data is not null && + transaction.Data.Value.Span.StartsWith(EvmObjectFormat.MAGIC) + ) + { + return false; + } + if (transaction.Initcodes?.Length >= Transaction.MaxInitcodeCount) return false; foreach (var initcode in transaction.Initcodes) diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index b9ee159c74a..f2a119d4a1b 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -56,6 +56,7 @@ public class Transaction public Signature? Signature { get; set; } public bool IsSigned => Signature is not null; public bool IsContractCreation => To is null; + public bool IsEofContractCreation => Initcodes is not null; public bool IsMessageCall => To is not null; private Hash256? _hash; diff --git a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs index f3e54899438..31b0a4f8f2e 100644 --- a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs +++ b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs @@ -8,6 +8,7 @@ using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; using Nethermind.Int256; +using Nethermind.Specs; namespace Nethermind.Evm; @@ -19,6 +20,7 @@ public static long Calculate(Transaction transaction, IReleaseSpec releaseSpec) result += DataCost(transaction, releaseSpec); result += CreateCost(transaction, releaseSpec); result += AccessListCost(transaction, releaseSpec); + result += EofInitCodeCost(transaction, releaseSpec); return result; } @@ -33,22 +35,43 @@ private static long CreateCost(Transaction transaction, IReleaseSpec releaseSpec return createCost; } + private static long EofInitCodeCost(Transaction transaction, IReleaseSpec releaseSpec) + { + if(releaseSpec.IsEofEnabled && transaction.IsEofContractCreation) + { + long initcodeCosts = 0; + foreach(var initcode in transaction.Initcodes) + { + initcodeCosts += CalculateCalldataCost(initcode, releaseSpec); + } + return initcodeCosts; + } + return 0; + } + private static long DataCost(Transaction transaction, IReleaseSpec releaseSpec) + { + Span data = transaction.Data.GetValueOrDefault().Span; + long dataCost = CalculateCalldataCost(data, releaseSpec); + + if (transaction.IsContractCreation && releaseSpec.IsEip3860Enabled) + { + dataCost += EvmPooledMemory.Div32Ceiling((UInt256)data.Length) * GasCostOf.InitCodeWord; + } + + return dataCost; + } + + private static long CalculateCalldataCost(Span data, IReleaseSpec releaseSpec) { long txDataNonZeroGasCost = releaseSpec.IsEip2028Enabled ? GasCostOf.TxDataNonZeroEip2028 : GasCostOf.TxDataNonZero; long dataCost = 0; - Span data = transaction.Data.GetValueOrDefault().Span; for (int i = 0; i < data.Length; i++) { dataCost += data[i] == 0 ? GasCostOf.TxDataZero : txDataNonZeroGasCost; } - if (transaction.IsContractCreation && releaseSpec.IsEip3860Enabled) - { - dataCost += EvmPooledMemory.Div32Ceiling((UInt256)data.Length) * GasCostOf.InitCodeWord; - } - return dataCost; } From 469bfb284553d971aebc1b9d47394d2dc30e1d35 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 6 Feb 2024 03:44:11 +0100 Subject: [PATCH 033/255] minor fix --- src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index e5a2c9c6e77..840a7c80b3d 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -55,6 +55,7 @@ private static bool ValidateTxType(Transaction transaction, IReleaseSpec release TxType.AccessList => releaseSpec.UseTxAccessLists, TxType.EIP1559 => releaseSpec.IsEip1559Enabled, TxType.Blob => releaseSpec.IsEip4844Enabled, + TxType.EofInitcodeTx => releaseSpec.IsEofEnabled, _ => false }; From c8501df73d134ca38431db7baf33c6cfb7a21b7d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 6 Feb 2024 04:40:58 +0100 Subject: [PATCH 034/255] fixed eof code deployment code --- .../Extensions/IntExtensions.cs | 33 +++++++++++++++++++ .../EvmObjectFormat/EofCodeValidator.cs | 1 + .../EvmObjectFormat/EofHeader.cs | 1 + .../Nethermind.Evm/VirtualMachine.cs | 31 ++++------------- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs b/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs index 71505417f89..e1aa2aca7f4 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs @@ -36,7 +36,36 @@ public static byte[] ToByteArray(this int value) return bytes; } + public static byte[] ToBigEndianByteArray(this ulong value) + { + byte[] bytes = BitConverter.GetBytes(value); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return bytes; + } + + public static byte[] ToBigEndianByteArray(this long value) + => ToBigEndianByteArray((ulong)value); + + + public static byte[] ToBigEndianByteArray(this uint value) + { + byte[] bytes = BitConverter.GetBytes(value); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return bytes; + } + public static byte[] ToBigEndianByteArray(this int value) + => ToBigEndianByteArray((uint)value); + + public static byte[] ToBigEndianByteArray(this ushort value) { byte[] bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) @@ -46,4 +75,8 @@ public static byte[] ToBigEndianByteArray(this int value) return bytes; } + + public static byte[] ToBigEndianByteArray(this short value) + => ToBigEndianByteArray((ushort)value); + } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 7d80fc45f9c..b96fb9e6c45 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -317,6 +317,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => header = new EofHeader { Version = VERSION, + PrefixSize = HEADER_TERMINATOR_OFFSET, TypeSection = typeSectionHeader, CodeSections = codeSectionHeader, ContainerSection = containerSectionHeader, diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index e498c6436f8..33a039404b5 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -11,6 +11,7 @@ namespace Nethermind.Evm.EOF; public struct EofHeader() { public required byte Version; + public required int PrefixSize; public required SectionHeader TypeSection; public required CompoundSectionHeader CodeSections; public required SectionHeader DataSection; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 505bfc754fa..6bf988ac3ab 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -455,37 +455,18 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (!isEof_unvalidated) { - Span bytecodeResult = new byte[container.Length + header.Value.DataSection.Size]; + Span bytecodeResult = new byte[container.Length + auxExtraData.Length]; // copy magic eof prefix int movingOffset = 0; - ReadOnlySpan magicSpan = container.Slice(0, EvmObjectFormat.MAGIC.Length + EvmObjectFormat.ONE_BYTE_LENGTH); - magicSpan.CopyTo(bytecodeResult); - movingOffset += magicSpan.Length; - - // copy typesection header - ReadOnlySpan typesectionHeaderSpan = container.Slice(header.Value.TypeSection.Start, header.Value.TypeSection.Size); - typesectionHeaderSpan.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += typesectionHeaderSpan.Length; - - // copy codesection header - ReadOnlySpan codesectionHeaderSpan = container.Slice(header.Value.CodeSections.Start, header.Value.CodeSections.Size); - codesectionHeaderSpan.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += codesectionHeaderSpan.Length; - - - // copy containersection header - ReadOnlySpan containerectionHeaderSpan = container.Slice(header.Value.ContainerSection.Value.Start, header.Value.ContainerSection.Value.Size); - containerectionHeaderSpan.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += containerectionHeaderSpan.Length; + ReadOnlySpan eofPrefix = container.Slice(header.Value.PrefixSize); + eofPrefix.CopyTo(bytecodeResult); + movingOffset += header.Value.PrefixSize - 3; // we move offset back by TERMINATOR + SIZE_OF_DATA_SECTION_HEADER // copy datasection header - bytecodeResult[movingOffset++] = (byte)EvmObjectFormat.Eof1.Separator.KIND_DATA; - byte[] newDataSectionLength = (auxExtraData.Length + header.Value.DataSection.Size).ToBigEndianByteArray(); + byte[] newDataSectionLength = ((ushort)(auxExtraData.Length + header.Value.DataSection.Size)).ToBigEndianByteArray(); newDataSectionLength.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += newDataSectionLength.Length; - - bytecodeResult[movingOffset++] = (byte)EvmObjectFormat.Eof1.Separator.TERMINATOR; + movingOffset = header.Value.PrefixSize; // copy type section ReadOnlySpan typesectionSpan = container.Slice(header.Value.TypeSection.Start, header.Value.TypeSection.Size); From 9c49a48b0f2d176ad21ea205eeb3b0c06680cc57 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 7 Feb 2024 18:09:32 +0100 Subject: [PATCH 035/255] fix build failure --- .../Extensions/Int16Extensions.cs | 32 +++++++++++++++++++ .../Extensions/Int64Extensions.cs | 6 +++- .../Extensions/IntExtensions.cs | 30 ----------------- 3 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 src/Nethermind/Nethermind.Core/Extensions/Int16Extensions.cs diff --git a/src/Nethermind/Nethermind.Core/Extensions/Int16Extensions.cs b/src/Nethermind/Nethermind.Core/Extensions/Int16Extensions.cs new file mode 100644 index 00000000000..67cc0acd737 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Extensions/Int16Extensions.cs @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Buffers.Binary; + +namespace Nethermind.Core.Extensions; + +public static class short16Extensions +{ + public static byte[] ToByteArray(this short value) + { + byte[] bytes = new byte[sizeof(short)]; + BinaryPrimitives.WriteInt16BigEndian(bytes, value); + return bytes; + } + + public static byte[] ToBigEndianByteArray(this ushort value) + { + byte[] bytes = BitConverter.GetBytes(value); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return bytes; + } + + public static byte[] ToBigEndianByteArray(this short value) + => ToBigEndianByteArray((ushort)value); + +} diff --git a/src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs b/src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs index 1306742283c..e8654d3d407 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs @@ -117,7 +117,7 @@ public static byte[] ToBigEndianByteArrayWithoutLeadingZeros(this long value) } } - public static byte[] ToBigEndianByteArray(this long value) + public static byte[] ToBigEndianByteArray(this ulong value) { byte[] bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) @@ -127,6 +127,10 @@ public static byte[] ToBigEndianByteArray(this long value) return bytes; } + + public static byte[] ToBigEndianByteArray(this long value) + => ToBigEndianByteArray((ulong)value); + public static void WriteBigEndian(this long value, Span output) { BinaryPrimitives.WriteInt64BigEndian(output, value); diff --git a/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs b/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs index e1aa2aca7f4..1ecd2adc0d8 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs @@ -36,21 +36,6 @@ public static byte[] ToByteArray(this int value) return bytes; } - public static byte[] ToBigEndianByteArray(this ulong value) - { - byte[] bytes = BitConverter.GetBytes(value); - if (BitConverter.IsLittleEndian) - { - Array.Reverse(bytes); - } - - return bytes; - } - - public static byte[] ToBigEndianByteArray(this long value) - => ToBigEndianByteArray((ulong)value); - - public static byte[] ToBigEndianByteArray(this uint value) { byte[] bytes = BitConverter.GetBytes(value); @@ -64,19 +49,4 @@ public static byte[] ToBigEndianByteArray(this uint value) public static byte[] ToBigEndianByteArray(this int value) => ToBigEndianByteArray((uint)value); - - public static byte[] ToBigEndianByteArray(this ushort value) - { - byte[] bytes = BitConverter.GetBytes(value); - if (BitConverter.IsLittleEndian) - { - Array.Reverse(bytes); - } - - return bytes; - } - - public static byte[] ToBigEndianByteArray(this short value) - => ToBigEndianByteArray((ushort)value); - } From 95bb439eb4823158977de795718f8c80f24fdb8d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 5 Mar 2024 21:02:20 +0100 Subject: [PATCH 036/255] rename opcodes --- .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 4 +-- .../EvmObjectFormat/EofCodeValidator.cs | 2 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 34 +++++++++---------- .../Nethermind.Evm/VirtualMachine.cs | 24 ++++++------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 41de9289432..33f8a0788c4 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -123,8 +123,8 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase Instruction.CALLF, Instruction.RETF, Instruction.JUMPF, - Instruction.CREATE3, - Instruction.CREATE4, + Instruction.EOFCREATE, + Instruction.TXCREATE, Instruction.RETURNCONTRACT, Instruction.DATASIZE, Instruction.DATACOPY, diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index b96fb9e6c45..32411074a0a 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -631,7 +631,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 5e6e2994047..1c898c94a6b 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Diagnostics.CodeAnalysis; using FastEnumUtility; using Nethermind.Core.Specs; using Nethermind.Evm.EOF; using Nethermind.Specs.Forks; +using System; +using System.Diagnostics.CodeAnalysis; namespace Nethermind.Evm { @@ -184,8 +184,8 @@ public enum Instruction : byte CALLF = 0xe3, RETF = 0xe4, JUMPF = 0xe5, - CREATE3 = 0xec, - CREATE4 = 0xed, + EOFCREATE = 0xec, + TXCREATE = 0xed, RETURNCONTRACT = 0xee, DATALOAD = 0xd0, DATALOADN = 0xd1, @@ -198,9 +198,9 @@ public enum Instruction : byte RETURNDATALOAD = 0xf7, // opcode value not spec-ed - CALL2 = 0xf8, - DELEGATECALL2 = 0xf9, // DelegateCallEnabled - STATICCALL2 = 0xfb, // StaticCallEnabled + EXTCALL = 0xf8, + EXTDELEGATECALL = 0xf9, // DelegateCallEnabled + EXTSTATICCALL = 0xfb, // StaticCallEnabled } public static class InstructionExtensions @@ -234,9 +234,9 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) 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.RETURNCONTRACT or Instruction.CREATE4 or Instruction.CREATE3 => IsEofContext, + Instruction.RETURNCONTRACT or Instruction.TXCREATE or Instruction.EOFCREATE => IsEofContext, Instruction.DATACOPY or Instruction.DATASIZE or Instruction.DATALOAD or Instruction.DATALOADN => IsEofContext, - Instruction.STATICCALL2 or Instruction.DELEGATECALL2 or Instruction.CALL2 => IsEofContext, + Instruction.EXTSTATICCALL or Instruction.EXTDELEGATECALL or Instruction.EXTCALL => IsEofContext, Instruction.RETURNDATALOAD => IsEofContext, Instruction.CALL => !IsEofContext, Instruction.CALLCODE => !IsEofContext, @@ -344,8 +344,8 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.REVERT => (2, 0, 0), Instruction.INVALID => (0, 0, 0), - Instruction.CREATE3 => (4, 1, 1), - Instruction.CREATE4 => (5, 1, 0), + Instruction.EOFCREATE => (4, 1, 1), + Instruction.TXCREATE => (5, 1, 0), Instruction.RETURNCONTRACT => (2, 2, 1), Instruction.DATALOAD => (1, 1, 0), Instruction.DATALOADN => (0, 1, 2), @@ -357,9 +357,9 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.DUPN => (null, null, 1), Instruction.EXCHANGE => (null, null, 1), - Instruction.CALL2 => (3, 1, 0), - Instruction.STATICCALL2 => (3, 1, 0), - Instruction.DELEGATECALL2 => (3, 1, 0), + Instruction.EXTCALL => (3, 1, 0), + Instruction.EXTSTATICCALL => (3, 1, 0), + Instruction.EXTDELEGATECALL => (3, 1, 0), _ => throw new NotImplementedException($"opcode {instruction} not implemented yet"), }; @@ -368,9 +368,9 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) spec ??= Frontier.Instance; return instruction switch { - Instruction.CALL2 => "CALL" , - Instruction.STATICCALL2 => "STATICCALL", // StaticCallEnabled - Instruction.DELEGATECALL2 => "DELEGATECALL", + Instruction.EXTCALL => "EXTCALL" , + Instruction.EXTSTATICCALL => "EXTSTATICCALL", // StaticCallEnabled + Instruction.EXTDELEGATECALL => "EXTDELEGATECALL", Instruction.PREVRANDAO when !isPostMerge => "DIFFICULTY", Instruction.RJUMP => spec.IsEofEnabled ? "RJUMP" : "BEGINSUB", Instruction.RJUMPI => spec.IsEofEnabled ? "RJUMPI" : "RETURNSUB", diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 6bf988ac3ab..9178954f4a8 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2056,8 +2056,8 @@ private CallResult ExecuteCode 0)) { @@ -2465,9 +2465,9 @@ private CallResult ExecuteCode(EvmState vmState, ref { ref readonly ExecutionEnvironment env = ref vmState.Env; - var currentContext = instruction == Instruction.CREATE3 ? ExecutionType.CREATE3: ExecutionType.CREATE4; + var currentContext = instruction == Instruction.EOFCREATE ? ExecutionType.CREATE3: ExecutionType.CREATE4; if (!UpdateGas(currentContext == ExecutionType.CREATE3 ? GasCostOf.Create3 : GasCostOf.Create4, ref gasAvailable)) // still undecided in EIP return (EvmExceptionType.OutOfGas, null); @@ -3073,14 +3073,14 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref ReadOnlyMemory initCode = ReadOnlyMemory.Empty; switch(instruction) { - case Instruction.CREATE3 : + case Instruction.EOFCREATE : { int initCodeIdx = codeSection[vmState.ProgramCounter]; SectionHeader containerSection = env.CodeInfo.ContainerOffset(initCodeIdx); initCode = env.CodeInfo.ContainerSection[containerSection.Start..containerSection.EndOffset]; break; } - case Instruction.CREATE4 : + case Instruction.TXCREATE : { byte[] initContainerHash = stack.PopWord256().ToArray(); var initcode = env.TxExecutionContext.InitCodes?.First( @@ -3105,7 +3105,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref if (initCode.Length > spec.MaxInitCodeSize) return (EvmExceptionType.InvalidCode, null); } - long gasCost = (instruction is Instruction.CREATE4 && spec.IsEip3860Enabled ? GasCostOf.InitCodeWord * EvmPooledMemory.Div32Ceiling((UInt256)initCode.Length) : 0) + + long gasCost = (instruction is Instruction.TXCREATE && spec.IsEip3860Enabled ? GasCostOf.InitCodeWord * EvmPooledMemory.Div32Ceiling((UInt256)initCode.Length) : 0) + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initCode.Length); if (!UpdateGas(gasCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); @@ -3119,7 +3119,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } - if (!EvmObjectFormat.IsValidEof(initCode.Span, instruction is Instruction.CREATE4, out _)) + if (!EvmObjectFormat.IsValidEof(initCode.Span, instruction is Instruction.TXCREATE, out _)) { // handle invalid Eof code _returnDataBuffer = Array.Empty(); @@ -3203,8 +3203,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref callEnv, instruction switch { - Instruction.CREATE3 => ExecutionType.CREATE3, - Instruction.CREATE4 => ExecutionType.CREATE4, + Instruction.EOFCREATE => ExecutionType.CREATE3, + Instruction.TXCREATE => ExecutionType.CREATE4, _ => throw new UnreachableException() }, false, From f385efbd89ade623812b05d09e4c69f9da9c2342 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 5 Mar 2024 21:40:57 +0100 Subject: [PATCH 037/255] fix building issues --- .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 6 +++--- src/Nethermind/Nethermind.Evm/EvmStack.cs | 8 ++++---- src/Nethermind/Nethermind.Evm/EvmState.cs | 2 +- .../TransactionProcessing/TransactionProcessor.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 9 ++++++++- .../Nethermind.JsonRpc/Data/TransactionForRpc.cs | 14 +++++++++++++- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index db87d6568a0..7a0b9d6b8fb 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -133,9 +133,9 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase Instruction.SWAPN, Instruction.DUPN, Instruction.EXCHANGE, - Instruction.CALL2, - Instruction.DELEGATECALL2, - Instruction.STATICCALL2, + Instruction.EXTCALL, + Instruction.EXTDELEGATECALL, + Instruction.EXTSTATICCALL, } ).ToArray(); diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 0a0223e514d..bcadbd7d16e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -411,12 +411,12 @@ public readonly bool Swap(int depth) public readonly bool Exchange(int n, int m) { - if (!EnsureDepth(n + m)) return false; + if (!EnsureDepth(n + m + 1)) return false; ref byte bytes = ref MemoryMarshal.GetReference(_bytes); - ref byte bottom = ref Unsafe.Add(ref bytes, (Head - n - m) * WordSize); - ref byte top = ref Unsafe.Add(ref bytes, (Head - n) * WordSize); + ref byte bottom = ref Unsafe.Add(ref bytes, (Head - n - m - 1) * WordSize); + ref byte top = ref Unsafe.Add(ref bytes, (Head - n - 1) * WordSize); Word buffer = Unsafe.ReadUnaligned(ref bottom); Unsafe.WriteUnaligned(ref bottom, Unsafe.ReadUnaligned(ref top)); @@ -424,7 +424,7 @@ public readonly bool Exchange(int n, int m) if (typeof(TTracing) == typeof(IsTracing)) { - Trace(n + m); + Trace(n + m + 1); } return true; diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index bb710928ad0..f8b7ee7a45f 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -38,7 +38,7 @@ public StackPool(int maxCallStackDepth = VirtualMachine.MaxCallDepth * 2) } private readonly Stack _dataStackPool = new(32); - private readonly Stack _returnStackPool = new(32); + private readonly Stack _returnStackPool = new(32); private int _dataStackPoolDepth; private int _returnStackPoolDepth; diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index f5a5eead2a3..d2e7ffeff0e 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -405,7 +405,7 @@ protected virtual ExecutionEnvironment BuildExecutionEnvironment( Address recipient = tx.GetRecipient(tx.IsContractCreation ? WorldState.GetNonce(tx.SenderAddress) : 0); if (recipient is null) ThrowInvalidDataException("Recipient has not been resolved properly before tx execution"); - TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes); + TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, tx.Initcodes); ICodeInfo codeInfo = tx.IsContractCreation ? CodeInfoFactory.CreateCodeInfo(tx.Data.AsArray(), spec) : VirtualMachine.GetCachedCodeInfo(WorldState, recipient, spec); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 59f14ee654c..9d54b82efaf 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1545,7 +1545,7 @@ private CallResult ExecuteCode(EvmState vmState, ref ReadOnlyMemory initCode = vmState.Memory.Load(in memoryPositionOfInitCode, initCodeLength); + if(initCode.Span.StartsWith(EvmObjectFormat.MAGIC)) + { + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + return (EvmExceptionType.None, null); + } + UInt256 balance = _state.GetBalance(env.ExecutingAccount); if (value > balance) { diff --git a/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpc.cs b/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpc.cs index 9806860fad7..c5233793d68 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpc.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Data/TransactionForRpc.cs @@ -131,6 +131,8 @@ public TransactionForRpc() { } public TxType Type { get; set; } + public byte[][] Initcodes { get; set;} + public IEnumerable? AccessList { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -165,7 +167,7 @@ public TransactionForRpc() { } AccessList = TryGetAccessList(), ChainId = chainId, DecodedMaxFeePerGas = MaxFeePerGas ?? 0, - Hash = Hash + Hash = Hash, }; if (tx.Supports1559) @@ -179,6 +181,11 @@ public TransactionForRpc() { } tx.BlobVersionedHashes = BlobVersionedHashes; } + if(tx.SupportsEofInitcode) + { + tx.Initcodes = Initcodes; + } + return tx; } @@ -219,6 +226,11 @@ public TransactionForRpc() { } tx.BlobVersionedHashes = BlobVersionedHashes; } + if (tx.SupportsEofInitcode) + { + tx.Initcodes = Initcodes; + } + return tx; } From 4ed08e2c1fe3f4abda482ed173a3fce8a0811ab6 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 8 Mar 2024 17:02:23 +0100 Subject: [PATCH 038/255] minor refactor --- .../Nethermind.Evm/CodeDepositHandler.cs | 2 +- .../Nethermind.Evm/CodeInfoFactory.cs | 2 +- .../EvmObjectFormat/EofCodeValidator.cs | 19 ++++++++++++++++--- .../Nethermind.Evm/VirtualMachine.cs | 4 ++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs index 2f855c7fa21..d1e04a3bafd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs @@ -48,7 +48,7 @@ public static bool IsValidWithEofRules(ReadOnlySpan code, int fromVersion) bool valid = code.Length >= 1 && codeVersion >= fromVersion && (isCodeEof ? // this needs test cases - EvmObjectFormat.IsValidEof(code, false, out _) : + EvmObjectFormat.IsValidEof(code, EvmObjectFormat.ValidationStrategy.Validate, out _) : fromVersion > 0 ? false : IsValidWithLegacyRules(code)); return valid; } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index e1924e8a222..b037077f97b 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -11,7 +11,7 @@ public static class CodeInfoFactory public static ICodeInfo CreateCodeInfo(byte[] code, IReleaseSpec spec) { CodeInfo codeInfo = new(code); - return spec.IsEofEnabled && EvmObjectFormat.IsValidEof(code, false, out EofHeader? header) + return spec.IsEofEnabled && EvmObjectFormat.IsValidEof(code, EvmObjectFormat.ValidationStrategy.Validate, out EofHeader? header) ? new EofCodeInfo(codeInfo, header.Value) : codeInfo; } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 32411074a0a..130ec12659e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -30,6 +30,13 @@ public Worklet(ushort position, ushort stackHeight) public ushort StackHeight; } + public enum ValidationStrategy + { + None = 0, + Validate = 1, + ValidateSubContainers = Validate | 2 + } + private interface IEofVersionHandler { bool ValidateBody(ReadOnlySpan code, EofHeader header); @@ -71,8 +78,14 @@ public static bool IsEof(ReadOnlySpan container, [NotNullWhen(true)] out i public static bool IsEofn(ReadOnlySpan container, byte version) => container.Length >= MAGIC.Length + 1 && container.StartsWith(MAGIC) && container[MAGIC.Length] == version; - public static bool IsValidEof(ReadOnlySpan container, bool validateSubContainers, [NotNullWhen(true)] out EofHeader? header) + public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy strategy, [NotNullWhen(true)] out EofHeader? header) { + if(strategy == ValidationStrategy.None) + { + header = null; + return true; + } + if (container.Length > VERSION_OFFSET && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) && handler.TryParseEofHeader(container, out header)) @@ -80,14 +93,14 @@ public static bool IsValidEof(ReadOnlySpan container, bool validateSubCont EofHeader h = header.Value; if (handler.ValidateBody(container, h)) { - if(validateSubContainers && header?.ContainerSection?.Count > 0) + if(strategy == ValidationStrategy.ValidateSubContainers && header?.ContainerSection?.Count > 0) { int containerSize = header.Value.ContainerSection.Value.Count; for (int i = 0; i < containerSize; i++) { ReadOnlySpan subContainer = container.Slice(header.Value.ContainerSection.Value.Start + header.Value.ContainerSection.Value[i].Start, header.Value.ContainerSection.Value[i].Size); - if(!IsValidEof(subContainer, validateSubContainers, out _)) + if(!IsValidEof(subContainer, strategy, out _)) { return false; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 9d54b82efaf..93a6da0dfbe 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -488,7 +488,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl bytecodeResultArray = bytecodeResult.ToArray(); } - bool invalidCode = !EvmObjectFormat.IsValidEof(bytecodeResultArray, false, out _) + bool invalidCode = !EvmObjectFormat.IsValidEof(bytecodeResultArray, EvmObjectFormat.ValidationStrategy.Validate, out _) && bytecodeResultArray.Length < spec.MaxCodeSize; long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray.Length, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) @@ -3096,7 +3096,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } - if (!EvmObjectFormat.IsValidEof(initCode.Span, instruction is Instruction.TXCREATE, out _)) + if (!EvmObjectFormat.IsValidEof(initCode.Span, instruction is Instruction.TXCREATE ? EvmObjectFormat.ValidationStrategy.ValidateSubContainers : EvmObjectFormat.ValidationStrategy.Validate, out _)) { // handle invalid Eof code _returnDataBuffer = Array.Empty(); From e7ea075fe968efad10118aee8f4ff7d7396276a8 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 20 Mar 2024 11:56:48 +0000 Subject: [PATCH 039/255] reorder and fix EXTDELEGATECALL --- .../Nethermind.Evm/VirtualMachine.cs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 93a6da0dfbe..3cecbe71c73 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2806,23 +2806,11 @@ private EvmExceptionType InstructionEofCall(); - stack.PushZero(); - return EvmExceptionType.None; - } - UInt256 callValue; switch (instruction) { @@ -2857,6 +2845,17 @@ private EvmExceptionType InstructionEofCall(); + stack.PushZero(); + return EvmExceptionType.None; + } + long gasExtra = 0L; if (!transferValue.IsZero) @@ -2877,7 +2876,7 @@ private EvmExceptionType InstructionEofCall= long.MaxValue) return EvmExceptionType.OutOfGas; From db3e816864b7f228c87901232e8a57946ba724ef Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 20 Mar 2024 12:19:55 +0000 Subject: [PATCH 040/255] another fix for EXTDELEGATECALL --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3cecbe71c73..2e6c044153a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2848,7 +2848,6 @@ private EvmExceptionType InstructionEofCall(); From 79edebb4d5f771c5cc36e012f82bbe28a0c189ec Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 20 Mar 2024 16:14:02 +0000 Subject: [PATCH 041/255] fix build --- src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index b037077f97b..dfe588013d8 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -3,15 +3,16 @@ using Nethermind.Core.Specs; using Nethermind.Evm.EOF; +using System; namespace Nethermind.Evm.CodeAnalysis; public static class CodeInfoFactory { - public static ICodeInfo CreateCodeInfo(byte[] code, IReleaseSpec spec) + public static ICodeInfo CreateCodeInfo(Memory code, IReleaseSpec spec) { CodeInfo codeInfo = new(code); - return spec.IsEofEnabled && EvmObjectFormat.IsValidEof(code, EvmObjectFormat.ValidationStrategy.Validate, out EofHeader? header) + return spec.IsEofEnabled && EvmObjectFormat.IsValidEof(code.Span, EvmObjectFormat.ValidationStrategy.Validate, out EofHeader? header) ? new EofCodeInfo(codeInfo, header.Value) : codeInfo; } From 3a6e5412ffa46a8b7719ff7c2b39a68defe60533 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 2 Apr 2024 08:04:29 +0000 Subject: [PATCH 042/255] update EOFCREATE implementation, CREATECONTRACT --- .../EvmObjectFormat/EofCodeValidator.cs | 25 ++++++++---- .../Nethermind.Evm/VirtualMachine.cs | 39 ++----------------- 2 files changed, 21 insertions(+), 43 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 130ec12659e..220c8ac3e6a 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -34,12 +34,13 @@ public enum ValidationStrategy { None = 0, Validate = 1, - ValidateSubContainers = Validate | 2 + ValidateSubContainers = Validate | 2, + ValidateFullBody = Validate | 4 } private interface IEofVersionHandler { - bool ValidateBody(ReadOnlySpan code, EofHeader header); + bool ValidateBody(ReadOnlySpan code, EofHeader header, ValidationStrategy strategy); bool TryParseEofHeader(ReadOnlySpan code, [NotNullWhen(true)] out EofHeader? header); } @@ -91,7 +92,7 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s && handler.TryParseEofHeader(container, out header)) { EofHeader h = header.Value; - if (handler.ValidateBody(container, h)) + if (handler.ValidateBody(container, h, strategy)) { if(strategy == ValidationStrategy.ValidateSubContainers && header?.ContainerSection?.Count > 0) { @@ -278,7 +279,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => if (containerSectionSize == 0) { - if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Empty Code Section are not allowed, containerSectionSize must be > 0 but found {containerSectionSize}"); + if (Logger.IsTrace) Logger.Trace($"EIP-3540 : Empty Container Section are not allowed, containerSectionSize must be > 0 but found {containerSectionSize}"); return false; } @@ -340,13 +341,13 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => return true; } - public bool ValidateBody(ReadOnlySpan container, EofHeader header) + public bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStrategy strategy) { int startOffset = header.TypeSection.Start; int calculatedCodeLength = header.TypeSection.Size + header.CodeSections.Size - + header.DataSection.Size + + (strategy == ValidationStrategy.ValidateFullBody ? header.DataSection.Size : 0) + (header.ContainerSection?.Size ?? 0); CompoundSectionHeader codeSections = header.CodeSections; ReadOnlySpan contractBody = container[startOffset..]; @@ -402,7 +403,7 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header) bool isNonReturning = typesection[sectionIdx * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSectionStartOffset, codeSectionSize); - if (!ValidateInstructions(sectionIdx, isNonReturning, typesection, code, header, validationQueue, out ushort jumpsCount)) + if (!ValidateInstructions(sectionIdx, isNonReturning, typesection, code, header, container, validationQueue, out ushort jumpsCount)) { ArrayPool.Shared.Return(visitedSections, true); return false; @@ -456,7 +457,7 @@ bool ValidateTypeSection(ReadOnlySpan types) return true; } - bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, Queue worklist, out ushort jumpsCount) + bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist, out ushort jumpsCount) { byte[] codeBitmap = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); byte[] jumpdests = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); @@ -660,6 +661,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value[initcodeSectionId].Start, header.ContainerSection.Value[initcodeSectionId].Size); + if(IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody, out _)) + { + if (Logger.IsTrace) Logger.Trace($"EIP-XXXX : EOFCREATE's immediate must be a valid Eof"); + return false; + } + } if (opcode is >= Instruction.PUSH0 and <= Instruction.PUSH32) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 2e6c044153a..f6c49d33632 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -450,45 +450,14 @@ public TransactionSubstate Run(EvmState state, IWorldState worl { Span bytecodeResult = new byte[container.Length + auxExtraData.Length]; - // copy magic eof prefix - int movingOffset = 0; - ReadOnlySpan eofPrefix = container.Slice(header.Value.PrefixSize); - eofPrefix.CopyTo(bytecodeResult); - movingOffset += header.Value.PrefixSize - 3; // we move offset back by TERMINATOR + SIZE_OF_DATA_SECTION_HEADER - - // copy datasection header - byte[] newDataSectionLength = ((ushort)(auxExtraData.Length + header.Value.DataSection.Size)).ToBigEndianByteArray(); - newDataSectionLength.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset = header.Value.PrefixSize; - - // copy type section - ReadOnlySpan typesectionSpan = container.Slice(header.Value.TypeSection.Start, header.Value.TypeSection.Size); - typesectionSpan.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += typesectionSpan.Length; - - // copy code section - ReadOnlySpan codesectionSpan = container.Slice(header.Value.CodeSections[0].Start, header.Value.CodeSections.Size); - codesectionSpan.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += codesectionSpan.Length; - - // copy container section - ReadOnlySpan containersectionSpan = container.Slice(header.Value.ContainerSection?[0].Start ?? 0, header.Value.ContainerSection?.Size ?? 0); - containersectionSpan.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += containersectionSpan.Length; - - // copy data section - ReadOnlySpan datasectionSpan = container.Slice(header.Value.DataSection.Start, header.Value.DataSection.Size); - datasectionSpan.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += datasectionSpan.Length; - + // copy old container + container.CopyTo(bytecodeResult); // copy aux data to dataSection - auxExtraData.CopyTo(bytecodeResult.Slice(movingOffset)); - movingOffset += auxExtraData.Length; - + auxExtraData.CopyTo(bytecodeResult[container.Length..]); bytecodeResultArray = bytecodeResult.ToArray(); } - bool invalidCode = !EvmObjectFormat.IsValidEof(bytecodeResultArray, EvmObjectFormat.ValidationStrategy.Validate, out _) + bool invalidCode = !EvmObjectFormat.IsValidEof(bytecodeResultArray, EvmObjectFormat.ValidationStrategy.ValidateFullBody, out _) && bytecodeResultArray.Length < spec.MaxCodeSize; long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray.Length, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) From ad8a2a5a2059c3515598ed10c6886e2e4cc52be9 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 3 Apr 2024 16:13:36 +0000 Subject: [PATCH 043/255] match spec changes --- .../Nethermind.Consensus/Messages/TxErrorMessages.cs | 7 +++++-- .../Nethermind.Consensus/Validators/TxValidator.cs | 10 ++++++++-- src/Nethermind/Nethermind.Evm/GasCostOf.cs | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Messages/TxErrorMessages.cs b/src/Nethermind/Nethermind.Consensus/Messages/TxErrorMessages.cs index 578a87a233d..4998cca480a 100644 --- a/src/Nethermind/Nethermind.Consensus/Messages/TxErrorMessages.cs +++ b/src/Nethermind/Nethermind.Consensus/Messages/TxErrorMessages.cs @@ -85,6 +85,9 @@ public const string InvalidCreateTxData public const string TooManyEofInitcodes = $"TooManyEofInitcodes: Eof initcodes count exceeded limit"; - public const string EofContractSizeTooBig - = "EofContractSizeTooBig: Eof initcode size exceeded"; + 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)"; } diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index c856ca7cb74..b77cb77d274 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -165,6 +165,12 @@ transaction.Data is not null && return false; } + if (transaction.Initcodes?.Length == 0) + { + error = TxErrorMessages.TooManyEofInitcodes; + return false; + } + if (transaction.Initcodes?.Length >= Transaction.MaxInitcodeCount) { error = TxErrorMessages.TooManyEofInitcodes; @@ -174,7 +180,7 @@ transaction.Data is not null && bool valid = true; foreach (var initcode in transaction.Initcodes) { - if (initcode?.Length >= spec.MaxInitCodeSize) + if (initcode?.Length >= spec.MaxInitCodeSize || initcode?.Length == 0) { valid = false; break; @@ -183,7 +189,7 @@ transaction.Data is not null && if(!valid) { - error = TxErrorMessages.EofContractSizeTooBig; + error = TxErrorMessages.EofContractSizeInvalid; return false; } diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index cd6aee9b8c0..9ec83e866e4 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -81,6 +81,6 @@ public static class GasCostOf public const long Dupn = 3; public const long Callf = 5; public const long Jumpf = 5; - public const long Retf = 4; + public const long Retf = 3; } } From 9592bc6b12b13dbb7856741c1be5af5b885e9af7 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 3 Apr 2024 16:16:50 +0000 Subject: [PATCH 044/255] minor refactor --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f6c49d33632..931627455ba 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -3054,18 +3054,18 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref if (!UpdateGas(gasCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? - if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients + if (!EvmObjectFormat.IsValidEof(initCode.Span, instruction is Instruction.TXCREATE ? EvmObjectFormat.ValidationStrategy.ValidateSubContainers : EvmObjectFormat.ValidationStrategy.Validate, out _)) { - // TODO: need a test for this + // handle invalid Eof code _returnDataBuffer = Array.Empty(); stack.PushZero(); return (EvmExceptionType.None, null); } - if (!EvmObjectFormat.IsValidEof(initCode.Span, instruction is Instruction.TXCREATE ? EvmObjectFormat.ValidationStrategy.ValidateSubContainers : EvmObjectFormat.ValidationStrategy.Validate, out _)) + // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? + if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients { - // handle invalid Eof code + // TODO: need a test for this _returnDataBuffer = Array.Empty(); stack.PushZero(); return (EvmExceptionType.None, null); From 3464617fd1e514c823b1af65e1fa8f085835ef1d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 3 Apr 2024 16:34:34 +0000 Subject: [PATCH 045/255] fix build issue --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 931627455ba..0daccb06fad 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -42,7 +42,7 @@ namespace Nethermind.Evm; using Int256; using Nethermind.Evm.EOF; -using Nethermind.Evm.Tracing.GethStyle.JavaScript; +using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript; using Org.BouncyCastle.Asn1.X509; using SectionHeader = EOF.SectionHeader; From c7ab88f3e96ab34100ef3f4cb127569acb6f7c45 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 15 Apr 2024 00:05:34 +0100 Subject: [PATCH 046/255] added EOF eip tests loader (wip) --- .../Ethereum.Blockchain.Test/EofTests.cs | 19 +++-- .../LoadEofTestsStrategy.cs | 72 +++++++++++++++++++ 2 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 src/Nethermind/Ethereum.Test.Base/LoadEofTestsStrategy.cs diff --git a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs index f4fad766299..10f09551575 100644 --- a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; +using System.Linq; using Ethereum.Test.Base; using NUnit.Framework; @@ -13,15 +14,19 @@ public class EOFTests : GeneralStateTestBase { // Uncomment when EOF tests are merged - // [TestCaseSource(nameof(LoadTests))] - // public void Test(GeneralStateTest test) - // { - // Assert.True(RunTest(test).Pass); - // } + [TestCaseSource(nameof(LoadTests))] + public void Test(GeneralStateTest test) + { + Assert.True(RunTest(test).Pass); + } public static IEnumerable LoadTests() { - var loader = new TestsSourceLoader(new LoadEipTestsStrategy(), "stEOF"); - return (IEnumerable)loader.LoadTests(); + var eip3540Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP3540").LoadTests()); + var eip3670Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP3670").LoadTests()); + var eip4200Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP4200").LoadTests()); + var eip4750Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP4750").LoadTests()); + var eip5450Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP5450").LoadTests()); + return eip3540Loader.Concat(eip3670Loader).Concat(eip4200Loader).Concat(eip4750Loader).Concat(eip5450Loader); } } diff --git a/src/Nethermind/Ethereum.Test.Base/LoadEofTestsStrategy.cs b/src/Nethermind/Ethereum.Test.Base/LoadEofTestsStrategy.cs new file mode 100644 index 00000000000..bffc302979a --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/LoadEofTestsStrategy.cs @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.IO; +using Ethereum.Test.Base.Interfaces; + +namespace Ethereum.Test.Base +{ + public class LoadEofTestsStrategy : ITestLoadStrategy + { + public IEnumerable Load(string testsDirectoryName, string wildcard = null) + { + IEnumerable testDirs; + if (!Path.IsPathRooted(testsDirectoryName)) + { + string testsDirectory = GetGeneralStateTestsDirectory(); + + + testDirs = Directory.EnumerateDirectories(testsDirectory, testsDirectoryName, new EnumerationOptions { RecurseSubdirectories = true }); + } + else + { + testDirs = new[] { testsDirectoryName }; + } + + List testJsons = new(); + foreach (string testDir in testDirs) + { + testJsons.AddRange(LoadTestsFromDirectory(testDir, wildcard)); + } + + return testJsons; + } + + private string GetGeneralStateTestsDirectory() + { + char pathSeparator = Path.AltDirectorySeparatorChar; + string currentDirectory = AppDomain.CurrentDomain.BaseDirectory; + + return currentDirectory.Remove(currentDirectory.LastIndexOf("src")) + $"src{pathSeparator}tests{pathSeparator}EIPTests{pathSeparator}StateTests{pathSeparator}stEOF"; + } + + private IEnumerable LoadTestsFromDirectory(string testDir, string wildcard) + { + List testsByName = new(); + IEnumerable testFiles = Directory.EnumerateFiles(testDir); + + foreach (string testFile in testFiles) + { + FileTestsSource fileTestsSource = new(testFile, wildcard); + try + { + var tests = fileTestsSource.LoadGeneralStateTests(); + foreach (GeneralStateTest blockchainTest in tests) + { + blockchainTest.Category = testDir; + } + + testsByName.AddRange(tests); + } + catch (Exception e) + { + testsByName.Add(new GeneralStateTest { Name = testFile, LoadFailure = $"Failed to load: {e}" }); + } + } + + return testsByName; + } + } +} From b1229078d673e03f5f0fb0e94028ec8bc01516c1 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 26 Apr 2024 14:32:45 +0100 Subject: [PATCH 047/255] fix build issue --- .../Nethermind.Evm/IntrinsicGasCalculator.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs index 7af64edbf3d..e716b01948d 100644 --- a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs +++ b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs @@ -12,6 +12,7 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Nethermind.Core.Extensions; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace Nethermind.Evm; @@ -42,10 +43,14 @@ private static long EofInitCodeCost(Transaction transaction, IReleaseSpec releas { if(releaseSpec.IsEofEnabled && transaction.IsEofContractCreation) { + long txDataNonZeroGasCost = + releaseSpec.IsEip2028Enabled ? GasCostOf.TxDataNonZeroEip2028 : GasCostOf.TxDataNonZero; + long initcodeCosts = 0; foreach(var initcode in transaction.Initcodes) { - initcodeCosts += CalculateCalldataCost(initcode, releaseSpec); + int totalZeros = initcode.AsSpan().CountZeros(); + initcodeCosts += totalZeros * GasCostOf.TxDataZero + (initcode.Length - totalZeros) * txDataNonZeroGasCost; } return initcodeCosts; } @@ -55,7 +60,7 @@ private static long EofInitCodeCost(Transaction transaction, IReleaseSpec releas private static long DataCost(Transaction transaction, IReleaseSpec releaseSpec) { Span data = transaction.Data.GetValueOrDefault().Span; - long dataCost = CalculateCalldataCost(data, releaseSpec); + long dataCost = CalculateCalldataCost(transaction, releaseSpec); if (transaction.IsContractCreation && releaseSpec.IsEip3860Enabled) { @@ -65,7 +70,7 @@ private static long DataCost(Transaction transaction, IReleaseSpec releaseSpec) return dataCost; } - private static long CalculateCalldataCost(Span data, IReleaseSpec releaseSpec) + private static long CalculateCalldataCost(Transaction transaction, IReleaseSpec releaseSpec) { long txDataNonZeroGasCost = releaseSpec.IsEip2028Enabled ? GasCostOf.TxDataNonZeroEip2028 : GasCostOf.TxDataNonZero; From dd8d6dbd84bc44a316db08bb250ef34a26a683f1 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 30 Apr 2024 10:25:46 +0100 Subject: [PATCH 048/255] minor refactors --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 +- .../EvmObjectFormat/EofCodeInfo.cs | 25 +++++++++---- src/Nethermind/Nethermind.Evm/EvmState.cs | 4 +-- .../Nethermind.Evm/ExecutionType.cs | 6 ++-- src/Nethermind/Nethermind.Evm/GasCostOf.cs | 3 +- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 6 ++-- .../Tracing/ParityStyle/ParityLikeTxTracer.cs | 12 +++---- .../Nethermind.Evm/VirtualMachine.cs | 36 ++++++++----------- 8 files changed, 49 insertions(+), 45 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 138be2c1733..66bb7c0ca15 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -54,7 +54,7 @@ public SectionHeader SectionOffset(int idx) throw new NotImplementedException(); } - public SectionHeader ContainerOffset(int idx) + public SectionHeader? ContainerOffset(int idx) { throw new NotImplementedException(); } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index b006ccd247f..0b960fb38df 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -5,6 +5,7 @@ using Nethermind.Core.Extensions; using Nethermind.Evm.EOF; using Nethermind.Evm.Precompiles; +using static System.Collections.Specialized.BitVector32; namespace Nethermind.Evm.CodeAnalysis; @@ -17,13 +18,26 @@ public class EofCodeInfo : ICodeInfo public IPrecompile? Precompile => _codeInfo.Precompile; public int Version => _header.Version; public ReadOnlyMemory TypeSection { get; } - public ReadOnlyMemory CodeSection { get; } + public ReadOnlyMemory CodeSection(int index) + { + var offset = SectionOffset(index); + return MachineCode.Slice(offset.Start, offset.Size); + } public ReadOnlyMemory DataSection { get; } - public ReadOnlyMemory ContainerSection { get; } + public ReadOnlyMemory ContainerSection(int index) + { + var offset = ContainerOffset(index); + if (offset is null) + return Memory.Empty; + else + { + return MachineCode.Slice(offset.Value.Start, offset.Value.Size); + } + } public SectionHeader SectionOffset(int sectionId) => _header.CodeSections[sectionId]; - public SectionHeader ContainerOffset(int containerId) => + public SectionHeader? ContainerOffset(int containerId) => _header.ContainerSection is null - ? throw new System.Diagnostics.UnreachableException() + ? null : _header.ContainerSection.Value[containerId]; public (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) { @@ -47,9 +61,6 @@ public EofCodeInfo(CodeInfo codeInfo, in EofHeader header) _codeInfo = codeInfo; _header = header; TypeSection = MachineCode.Slice(_header.TypeSection.Start, _header.TypeSection.Size); - CodeSection = MachineCode.Slice(_header.CodeSections.Start, _header.CodeSections.Size); DataSection = MachineCode.Slice(_header.DataSection.Start, _header.DataSection.Size); - ContainerSection = _header.ContainerSection is null ? null - : MachineCode.Slice(_header.ContainerSection.Value.Start, _header.ContainerSection.Value.Size); } } diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index f8b7ee7a45f..400fc2fd23c 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -223,8 +223,8 @@ public Address From case ExecutionType.CALLCODE: case ExecutionType.CREATE: case ExecutionType.CREATE2: - case ExecutionType.CREATE3: - case ExecutionType.CREATE4: + case ExecutionType.EOFCREATE: + case ExecutionType.TXCREATE: case ExecutionType.TRANSACTION: return Env.Caller; case ExecutionType.DELEGATECALL: diff --git a/src/Nethermind/Nethermind.Evm/ExecutionType.cs b/src/Nethermind/Nethermind.Evm/ExecutionType.cs index 68786733c8a..f0c9d25cbb4 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionType.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionType.cs @@ -12,7 +12,7 @@ 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.CREATE3 or ExecutionType.CREATE4; + 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) => @@ -29,8 +29,8 @@ public enum ExecutionType DELEGATECALL, CREATE, CREATE2, - CREATE3, - CREATE4, + EOFCREATE, + TXCREATE, } // ReSharper restore IdentifierTypo InconsistentNaming } diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index 9ec83e866e4..1e23f9e7a9f 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -68,8 +68,7 @@ public static class GasCostOf public const long DataCopy = 3; public const long DataSize = 2; public const long ReturnContract = 0; - public const long Create3 = 32000; - public const long Create4 = 32000; + public const long EofCreate = 32000; public const long ReturnDataLoad = 3; diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index 71e9847b25d..03ff69ce4d1 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -15,11 +15,11 @@ public interface ICodeInfo IPrecompile? Precompile { get; } bool IsPrecompile => Precompile is not null; ReadOnlyMemory TypeSection => Memory.Empty; - ReadOnlyMemory CodeSection => MachineCode; + ReadOnlyMemory CodeSection(int _) => MachineCode; ReadOnlyMemory DataSection => Memory.Empty; - ReadOnlyMemory ContainerSection => Memory.Empty; + ReadOnlyMemory ContainerSection(int _) => Memory.Empty; SectionHeader SectionOffset(int idx); - SectionHeader ContainerOffset(int idx); + SectionHeader? ContainerOffset(int idx); (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); bool ValidateJump(int destination, bool isSubroutine); } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs index 4ebc3f2c561..a7b9a36f0d2 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs @@ -74,8 +74,8 @@ private static string GetCallType(ExecutionType executionType) return "call"; case ExecutionType.CREATE: case ExecutionType.CREATE2: - case ExecutionType.CREATE3: - case ExecutionType.CREATE4: + case ExecutionType.EOFCREATE: + case ExecutionType.TXCREATE: return "create"; case ExecutionType.CALL: return "call"; @@ -98,8 +98,8 @@ private static string GetActionType(ExecutionType executionType) return "call"; case ExecutionType.CREATE: case ExecutionType.CREATE2: - case ExecutionType.CREATE3: - case ExecutionType.CREATE4: + case ExecutionType.EOFCREATE: + case ExecutionType.TXCREATE: return "create"; case ExecutionType.CALL: return "call"; @@ -423,9 +423,9 @@ public override void ReportAction(long gas, UInt256 value, Address from, Address return "create"; case ExecutionType.CREATE2: return "create2"; - case ExecutionType.CREATE3: + case ExecutionType.EOFCREATE: return "create3"; - case ExecutionType.CREATE4: + case ExecutionType.TXCREATE: return "create4"; default: return null; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 945c8ea503f..de6f78395b9 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -441,12 +441,11 @@ public TransactionSubstate Run(EvmState state, IWorldState worl { int containerIndex = callResult.Output.ContainerIndex.Value; ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; - (int start, int size) = previousState.Env.CodeInfo.ContainerOffset(containerIndex); - ReadOnlySpan container = previousState.Env.CodeInfo.ContainerSection.Slice(start, size).Span; - bool isEof_unvalidated = !EvmObjectFormat.TryExtractHeader(container, out EofHeader? header); + ReadOnlySpan container = previousState.Env.CodeInfo.ContainerSection(containerIndex).Span; + bool isEof_invalidated = !EvmObjectFormat.TryExtractHeader(container, out EofHeader? header) && header?.DataSection.Size != auxExtraData.Length; byte[] bytecodeResultArray = null; - if (!isEof_unvalidated) + if (!isEof_invalidated) { Span bytecodeResult = new byte[container.Length + auxExtraData.Length]; @@ -912,7 +911,7 @@ private CallResult ExecuteCode(EvmState vmState, ref { ref readonly ExecutionEnvironment env = ref vmState.Env; - var currentContext = instruction == Instruction.EOFCREATE ? ExecutionType.CREATE3: ExecutionType.CREATE4; - if (!UpdateGas(currentContext == ExecutionType.CREATE3 ? GasCostOf.Create3 : GasCostOf.Create4, ref gasAvailable)) // still undecided in EIP + var currentContext = instruction == Instruction.EOFCREATE ? ExecutionType.EOFCREATE: ExecutionType.TXCREATE; + if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); if (!stack.PopUInt256(out UInt256 value) || @@ -3014,12 +3012,14 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref !stack.PopUInt256(out UInt256 dataSize)) return (EvmExceptionType.StackUnderflow, null); + if (!UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataSize)) return (EvmExceptionType.OutOfGas, null); + ReadOnlyMemory initCode = ReadOnlyMemory.Empty; switch(instruction) { case Instruction.EOFCREATE : { - int initCodeIdx = codeSection[vmState.ProgramCounter]; + int initCodeIdx = codeSection[vmState.ProgramCounter++]; SectionHeader containerSection = env.CodeInfo.ContainerOffset(initCodeIdx); initCode = env.CodeInfo.ContainerSection[containerSection.Start..containerSection.EndOffset]; break; @@ -3054,14 +3054,6 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref if (!UpdateGas(gasCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - if (!EvmObjectFormat.IsValidEof(initCode.Span, instruction is Instruction.TXCREATE ? EvmObjectFormat.ValidationStrategy.ValidateSubContainers : EvmObjectFormat.ValidationStrategy.Validate, out _)) - { - // handle invalid Eof code - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients { @@ -3071,6 +3063,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } + Span calldata = vmState.Memory.LoadSpan(dataOffset, dataSize); + UInt256 balance = _state.GetBalance(env.ExecutingAccount); if (value > balance) { @@ -3138,7 +3132,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref executingAccount: contractAddress, codeSource: null, codeInfo: codeinfo, - inputData: default, + inputData: calldata.ToArray(), transferValue: value, value: value ); @@ -3147,8 +3141,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref callEnv, instruction switch { - Instruction.EOFCREATE => ExecutionType.CREATE3, - Instruction.TXCREATE => ExecutionType.CREATE4, + Instruction.EOFCREATE => ExecutionType.EOFCREATE, + Instruction.TXCREATE => ExecutionType.TXCREATE, _ => throw new UnreachableException() }, false, From 7e8b5ae097c60e60d96c7c4b480ee3de9508951a Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 30 Apr 2024 23:18:48 +0100 Subject: [PATCH 049/255] fix build issue --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index de6f78395b9..49164c09ccc 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -876,10 +876,8 @@ private CallResult ExecuteCode codeSection = env.CodeInfo.CodeSection.Span; + ReadOnlySpan codeSection = env.CodeInfo.CodeSection(0).Span; ReadOnlySpan dataSection = env.CodeInfo.DataSection.Span; - ReadOnlySpan typeSection = env.CodeInfo.TypeSection.Span; - ReadOnlySpan containerSection = env.CodeInfo.ContainerSection.Span; EvmExceptionType exceptionType = EvmExceptionType.None; bool isRevert = false; @@ -3020,8 +3018,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref case Instruction.EOFCREATE : { int initCodeIdx = codeSection[vmState.ProgramCounter++]; - SectionHeader containerSection = env.CodeInfo.ContainerOffset(initCodeIdx); - initCode = env.CodeInfo.ContainerSection[containerSection.Start..containerSection.EndOffset]; + initCode = env.CodeInfo.ContainerSection(initCodeIdx); break; } case Instruction.TXCREATE : From d07df5f1ffdeb458f846f4bfaad61b99d086be70 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 12 Jun 2024 05:39:23 +0100 Subject: [PATCH 050/255] minor refactors --- .../EvmObjectFormat/EofCodeValidator.cs | 125 +++++++++--------- src/Nethermind/Nethermind.Evm/Instruction.cs | 1 - .../Nethermind.Evm/VirtualMachine.cs | 9 +- 3 files changed, 62 insertions(+), 73 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index f24e597bcf4..5f32cc545d2 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -198,19 +198,19 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => // we need to be able to parse header + minimum section lenghts if (container.Length < MINIMUM_SIZE) { - if (Logger.IsTrace) Logger.Trace($"EOF : Eof{VERSION}, Code is too small to be valid code"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); return false; } if (!container.StartsWith(MAGIC)) { - if (Logger.IsTrace) Logger.Trace($"EOF : Code doesn't start with Magic byte sequence expected {MAGIC.ToHexString(true)} "); + if (Logger.IsTrace) Logger.Trace($"EOF: 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 : Code is not Eof version {VERSION}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Code is not Eof version {VERSION}"); return false; } @@ -220,20 +220,20 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => int TYPESECTION_HEADER_ENDOFFSET = TYPESECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; if (container[TYPESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_TYPE) { - if (Logger.IsTrace) Logger.Trace($"EOF : Code is not Eof version {VERSION}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Code is not Eof version {VERSION}"); return false; } sectionSizes.TypeSectionSize = GetUInt16(container, TYPESECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); if (sectionSizes.TypeSectionSize < MINIMUM_TYPESECTION_SIZE) { - if (Logger.IsTrace) Logger.Trace($"EOF : TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); + if (Logger.IsTrace) Logger.Trace($"EOF: TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); return false; } int CODESECTION_HEADER_STARTOFFSET = TYPESECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; if (container[CODESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_CODE) { - if (Logger.IsTrace) Logger.Trace($"EOF : Eof{VERSION}, Code header is not well formatted"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code header is not well formatted"); return false; } @@ -241,7 +241,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => sectionSizes.CodeSectionSize = (ushort)(numberOfCodeSections * TWO_BYTE_LENGTH); if (numberOfCodeSections > MAXIMUM_NUM_CODE_SECTIONS) { - if (Logger.IsTrace) Logger.Trace($"EOF : code sections count must not exceed {MAXIMUM_NUM_CODE_SECTIONS}"); + if (Logger.IsTrace) Logger.Trace($"EOF: code sections count must not exceed {MAXIMUM_NUM_CODE_SECTIONS}"); return false; } @@ -254,7 +254,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => if (codeSectionSize == 0) { - if (Logger.IsTrace) Logger.Trace($"EOF : Empty Code Section are not allowed, CodeSectionSize must be > 0 but found {codeSectionSize}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Empty Code Section are not allowed, CodeSectionSize must be > 0 but found {codeSectionSize}"); return false; } @@ -271,7 +271,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * TWO_BYTE_LENGTH); if (numberOfContainerSections is > MAXIMUM_NUM_CONTAINER_SECTIONS or 0) { - if (Logger.IsTrace) Logger.Trace($"EOF : code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); + if (Logger.IsTrace) Logger.Trace($"EOF: code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); return false; } @@ -284,7 +284,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => if (containerSectionSize == 0) { - if (Logger.IsTrace) Logger.Trace($"EOF : Empty Container Section are not allowed, containerSectionSize must be > 0 but found {containerSectionSize}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Empty Container Section are not allowed, containerSectionSize must be > 0 but found {containerSectionSize}"); return false; } @@ -298,7 +298,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => int DATASECTION_HEADER_ENDOFFSET = DATASECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; if (container[DATASECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_DATA) { - if (Logger.IsTrace) Logger.Trace($"EOF : Eof{VERSION}, Code header is not well formatted"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code header is not well formatted"); return false; } sectionSizes.DataSectionSize = GetUInt16(container, DATASECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); @@ -307,7 +307,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => int HEADER_TERMINATOR_OFFSET = DATASECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; if (container[HEADER_TERMINATOR_OFFSET] != (byte)Separator.TERMINATOR) { - if (Logger.IsTrace) Logger.Trace($"EOF : Eof{VERSION}, Code header is not well formatted"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code header is not well formatted"); return false; } @@ -362,25 +362,25 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida if (header.ContainerSection?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) { // move this check where `header.ExtraContainers.Count` is parsed - if (Logger.IsTrace) Logger.Trace($"EOF : initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSection?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSection?.Count}"); return false; } if (contractBody.Length != calculatedCodeLength) { - if (Logger.IsTrace) Logger.Trace("EOF : SectionSizes indicated in bundled header are incorrect, or ContainerCode is incomplete"); + if (Logger.IsTrace) Logger.Trace("EOF: SectionSizes indicated in bundled header are incorrect, or ContainerCode is incomplete"); return false; } if (strategy == ValidationStrategy.ValidateFullBody && header.DataSection.Size != dataBody.Length) { - if (Logger.IsTrace) Logger.Trace("EOF : DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + if (Logger.IsTrace) Logger.Trace("EOF: DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); return false; } if (codeSections.Count == 0 || codeSections.SubSectionsSizes.Any(size => size == 0)) { - if (Logger.IsTrace) Logger.Trace($"EOF : CodeSection size must follow a CodeSection, CodeSection length was {codeSections.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: CodeSection size must follow a CodeSection, CodeSection length was {codeSections.Count}"); return false; } @@ -450,19 +450,19 @@ bool ValidateTypeSection(ReadOnlySpan types) if (inputCount > INPUTS_MAX) { - if (Logger.IsTrace) Logger.Trace("EOF : Too many inputs"); + if (Logger.IsTrace) Logger.Trace("EOF: Too many inputs"); return false; } if (outputCount > OUTPUTS_MAX && outputCount != NON_RETURNING) { - if (Logger.IsTrace) Logger.Trace("EOF : Too many outputs"); + if (Logger.IsTrace) Logger.Trace("EOF: Too many outputs"); return false; } if (maxStackHeight > MAX_STACK_HEIGHT) { - if (Logger.IsTrace) Logger.Trace("EOF : Stack depth too high"); + if (Logger.IsTrace) Logger.Trace("EOF: Stack depth too high"); return false; } } @@ -485,7 +485,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : Static Relative Jump Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {opcode.FastToString()} Argument underflow"); return false; } @@ -502,7 +502,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : Static Relative Jump Destination outside of Code bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: {opcode.FastToString()} Destination outside of Code bounds"); return false; } @@ -515,7 +515,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : JUMPF Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.JUMPF} Argument underflow"); return false; } @@ -523,7 +523,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= header.CodeSections.Count) { - if (Logger.IsTrace) Logger.Trace($"EOF : JUMPF to unknown code section"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.JUMPF} to unknown code section"); return false; } @@ -531,7 +531,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : {opcode.FastToString()} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {opcode.FastToString()} Argument underflow"); return false; } BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); @@ -552,9 +552,9 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) + if (postInstructionByte + ONE_BYTE_LENGTH + TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : Static Relative Jumpv Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RJUMPV} Argument underflow"); return false; } @@ -562,13 +562,13 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : jumpv jumptable underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RJUMPV} jumptable underflow"); return false; } @@ -579,7 +579,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : Static Relative Jumpv Destination outside of Code bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RJUMPV} Destination outside of Code bounds"); return false; } BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpdests, ref rjumpdest); @@ -591,7 +591,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : CALLF Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.CALLF} Argument underflow"); return false; } @@ -599,14 +599,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= header.CodeSections.Count) { - if (Logger.IsTrace) Logger.Trace($"EOF : Invalid Section Id"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.CALLF} Invalid Section Id"); return false; } byte targetSectionOutputCount = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; if (targetSectionOutputCount == 0x80) { - if (Logger.IsTrace) Logger.Trace($"EOF : CALLF into non-returning function"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.CALLF} into non-returning function"); return false; } @@ -616,7 +616,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : DATALOADN Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.DATALOADN} Argument underflow"); return false; } ushort dataSectionOffset = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); - if (dataSectionOffset * 32 >= header.DataSection.Size) + if (dataSectionOffset + 32 >= header.DataSection.Size) { - - if (Logger.IsTrace) Logger.Trace($"EOF : DATALOADN's immediate argument must be less than datasection.Length / 32 i.e : {header.DataSection.Size / 32}"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.DATALOADN}'s immediate argument must be less than datasection.Length / 32 i.e: {header.DataSection.Size / 32}"); return false; } BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); @@ -643,15 +642,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : DATALOADN Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RETURNCONTRACT} Argument underflow"); return false; } ushort containerId = code[postInstructionByte]; - if (containerId >= 0 && containerId < header.ContainerSection?.Count) + if ( containerId >= header.ContainerSection?.Count) { - - if (Logger.IsTrace) Logger.Trace($"EOF : RETURNCONTRACT's immediate argument must be less than containersection.Count i.e : {header.ContainerSection?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {header.ContainerSection?.Count}"); return false; } BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); @@ -661,7 +659,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : CREATE3 Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.EOFCREATE} Argument underflow"); return false; } @@ -670,14 +668,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan= header.ContainerSection?.Count) { - if (Logger.IsTrace) Logger.Trace($"EOF : CREATE3's immediate must falls within the Containers' range available, i.e : {header.CodeSections.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {header.CodeSections.Count}"); return false; } ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value[initcodeSectionId].Start, header.ContainerSection.Value[initcodeSectionId].Size); if(IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody, out _)) { - if (Logger.IsTrace) Logger.Trace($"EOF : EOFCREATE's immediate must be a valid Eof"); + if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.EOFCREATE}'s immediate must be a valid Eof"); return false; } @@ -688,7 +686,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : PC Reached out of bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: {opcode.FastToString()} PC Reached out of bounds"); return false; } BitmapHelper.HandleNumbits(len, codeBitmap, ref postInstructionByte); @@ -698,14 +696,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : PC Reached out of bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: PC Reached out of bounds"); return false; } bool result = !BitmapHelper.CheckCollision(codeBitmap, jumpdests); if (!result) { - if (Logger.IsTrace) Logger.Trace($"EOF : Invalid Jump destination"); + if (Logger.IsTrace) Logger.Trace($"EOF: Invalid Jump destination"); } if (!ValidateStackState(sectionId, code, typesection, jumpsCount)) @@ -752,7 +750,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea { if (worklet.StackHeight != recordedStackHeight[worklet.Position] - 1) { - if (Logger.IsTrace) Logger.Trace($"EOF : Branch joint line has invalid stack height"); + if (Logger.IsTrace) Logger.Trace($"EOF: Branch joint line has invalid stack height"); return false; } break; @@ -777,7 +775,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (worklet.StackHeight + maxStackHeight - inputs > MAX_STACK_HEIGHT) { - if (Logger.IsTrace) Logger.Trace($"EOF : stack head during callf must not exceed {MAX_STACK_HEIGHT}"); + if (Logger.IsTrace) Logger.Trace($"EOF: stack head during callf must not exceed {MAX_STACK_HEIGHT}"); return false; } break; @@ -802,7 +800,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (worklet.StackHeight < inputs) { - if (Logger.IsTrace) Logger.Trace($"EOF : Stack Underflow required {inputs} but found {worklet.StackHeight}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Stack Underflow required {inputs} but found {worklet.StackHeight}"); return false; } @@ -815,13 +813,13 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea { if (curr_outputs < outputs) { - if (Logger.IsTrace) Logger.Trace($"EOF : Output Count {outputs} must be less or equal than sectionId {sectionId} output count {curr_outputs}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Output Count {outputs} must be less or equal than sectionId {sectionId} output count {curr_outputs}"); return false; } if (worklet.StackHeight != curr_outputs + inputs - outputs) { - if (Logger.IsTrace) Logger.Trace($"EOF : Stack Height must {curr_outputs + inputs - outputs} but found {worklet.StackHeight}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Stack Height must {curr_outputs + inputs - outputs} but found {worklet.StackHeight}"); return false; } @@ -860,18 +858,17 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } worklet.Position = posPostInstruction; + if (opcode is Instruction.RJUMP) + { + break; + } if (opcode.IsTerminating()) { - if(opcode is Instruction.RJUMP) - { - break; - } - var expectedHeight = opcode is Instruction.RETF ? typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] : worklet.StackHeight; if (expectedHeight != worklet.StackHeight) { - if (Logger.IsTrace) Logger.Trace($"EOF : Stack state invalid required height {expectedHeight} but found {worklet.StackHeight}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Stack state invalid required height {expectedHeight} but found {worklet.StackHeight}"); return false; } break; @@ -879,7 +876,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea else if (worklet.Position >= code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF : Invalid code, reached end of code without a terminating instruction"); + if (Logger.IsTrace) Logger.Trace($"EOF: Invalid code, reached end of code without a terminating instruction"); return false; } } @@ -887,20 +884,20 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (unreachedBytes > 0) { - if (Logger.IsTrace) Logger.Trace($"EOF : bytecode has unreachable segments"); + if (Logger.IsTrace) Logger.Trace($"EOF: bytecode has unreachable segments"); return false; } if (peakStackHeight != suggestedMaxHeight) { - if (Logger.IsTrace) Logger.Trace($"EOF : Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); return false; } bool result = peakStackHeight <= MAX_STACK_HEIGHT; if (!result) { - if (Logger.IsTrace) Logger.Trace($"EOF : stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); + if (Logger.IsTrace) Logger.Trace($"EOF: stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); return false; } return result; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 06b4d88a1d8..7514161be0b 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -213,7 +213,6 @@ public static int GetImmediateCount(this Instruction instruction, bool IsEofCont { Instruction.RETF or Instruction.INVALID or Instruction.STOP or Instruction.RETURN or Instruction.REVERT => true, Instruction.JUMPF or Instruction.RETURNCONTRACT => true, - Instruction.RJUMP => true, // Instruction.SELFDESTRUCT => true _ => false }; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 40c19f168ca..3a1d249e1e5 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2225,7 +2225,6 @@ private CallResult ExecuteCode Date: Mon, 17 Jun 2024 12:42:51 +0100 Subject: [PATCH 051/255] * reimplemented Eip5450 * drafted new txCreate behavior * removed old txCreate Behavior --- .../Validators/TxValidator.cs | 49 +-- .../Builders/TransactionBuilder.cs | 6 - src/Nethermind/Nethermind.Core/Transaction.cs | 15 - src/Nethermind/Nethermind.Core/TxType.cs | 1 - .../Nethermind.Evm/CodeDepositHandler.cs | 4 +- .../Nethermind.Evm/CodeInfoFactory.cs | 34 +- .../Nethermind.Evm/CodeInfoRepository.cs | 88 ++--- .../EvmObjectFormat/EofCodeInfo.cs | 6 +- .../EvmObjectFormat/EofCodeValidator.cs | 338 ++++++++++-------- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 1 + .../Nethermind.Evm/ICodeInfoRepository.cs | 4 +- .../Nethermind.Evm/IntrinsicGasCalculator.cs | 19 - .../TransactionProcessor.cs | 43 ++- .../Nethermind.Evm/TxExecutionContext.cs | 5 +- .../Nethermind.Evm/VirtualMachine.cs | 12 +- .../Eth/TransactionForRpc.cs | 12 - .../OverridableCodeInfoRepository.cs | 10 +- .../Nethermind.Serialization.Rlp/TxDecoder.cs | 31 +- 18 files changed, 311 insertions(+), 367 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index b77cb77d274..258ec3d18d0 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -48,8 +48,7 @@ public bool IsWellFormed(Transaction transaction, IReleaseSpec releaseSpec, out && ValidateChainId(transaction, ref error) && ValidateWithError(Validate1559GasFields(transaction, releaseSpec), TxErrorMessages.InvalidMaxPriorityFeePerGas, ref error) && ValidateWithError(Validate3860Rules(transaction, releaseSpec), TxErrorMessages.ContractSizeTooBig, ref error) - && Validate4844Fields(transaction, ref error) - && ValidateMegaEofRules(transaction, releaseSpec, ref error); + && Validate4844Fields(transaction, ref error); } private static bool Validate3860Rules(Transaction transaction, IReleaseSpec releaseSpec) => @@ -63,7 +62,6 @@ private static bool ValidateTxType(Transaction transaction, IReleaseSpec release TxType.AccessList => releaseSpec.UseTxAccessLists, TxType.EIP1559 => releaseSpec.IsEip1559Enabled, TxType.Blob => releaseSpec.IsEip4844Enabled, - TxType.EofInitcodeTx => releaseSpec.IsEofEnabled, _ => false }; @@ -151,51 +149,6 @@ private bool ValidateSignature(Transaction tx, IReleaseSpec spec) return !spec.ValidateChainId; } - - private static bool ValidateMegaEofRules(Transaction transaction, IReleaseSpec spec, ref string error) - { - if (!spec.IsEofEnabled) return true; - - if (transaction.To is null && - transaction.Data is not null && - transaction.Data.Value.Span.StartsWith(EvmObjectFormat.MAGIC) - ) - { - error = TxErrorMessages.InvalidCreateTxData; - return false; - } - - if (transaction.Initcodes?.Length == 0) - { - error = TxErrorMessages.TooManyEofInitcodes; - return false; - } - - if (transaction.Initcodes?.Length >= Transaction.MaxInitcodeCount) - { - error = TxErrorMessages.TooManyEofInitcodes; - return false; - } - - bool valid = true; - foreach (var initcode in transaction.Initcodes) - { - if (initcode?.Length >= spec.MaxInitCodeSize || initcode?.Length == 0) - { - valid = false; - break; - } - } - - if(!valid) - { - error = TxErrorMessages.EofContractSizeInvalid; - return false; - } - - return true; - } - private static bool Validate4844Fields(Transaction transaction, ref string error) { // Execution-payload version verification diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs index e44bc29b48f..a06da3f81ef 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs @@ -27,12 +27,6 @@ public TransactionBuilder() }; } - public TransactionBuilder WithEofInitcodes(byte[][] initcodes) - { - TestObjectInternal.Initcodes = initcodes; - return this; - } - public TransactionBuilder WithNonce(UInt256 nonce) { TestObjectInternal.Nonce = nonce; diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index 5c187b4fd55..bacb5c5561f 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -21,7 +21,6 @@ public class Transaction { public const int BaseTxGasCost = 21000; public const int MaxInitcodeCount = 256; - public ulong? ChainId { get; set; } /// @@ -46,17 +45,14 @@ public class Transaction public bool SupportsAccessList => Type >= TxType.AccessList && Type != TxType.DepositTx; public bool Supports1559 => Type >= TxType.EIP1559 && Type != TxType.DepositTx; public bool SupportsBlobs => Type == TxType.Blob && Type != TxType.DepositTx; - public bool SupportsEofInitcode => Type == TxType.EofInitcodeTx && Type != TxType.DepositTx; public long GasLimit { get; set; } public Address? To { get; set; } public UInt256 Value { get; set; } public Memory? Data { get; set; } - public byte[][]? Initcodes { get; set; } public Address? SenderAddress { get; set; } public Signature? Signature { get; set; } public bool IsSigned => Signature is not null; public bool IsContractCreation => To is null; - public bool IsEofContractCreation => Initcodes is not null; public bool IsMessageCall => To is not null; private Hash256? _hash; @@ -228,16 +224,6 @@ public string ToString(string indent) builder.AppendLine($"{indent}{nameof(BlobVersionedHashes)}: {BlobVersionedHashes?.Length}"); } - if (SupportsEofInitcode) - { - builder.AppendLine($"{indent}{nameof(Initcodes)}: ["); - foreach (var initcode in Initcodes!) - { - builder.AppendLine($"{indent}{indent}{initcode.ToHexString()}"); - } - builder.AppendLine($"]"); - } - return builder.ToString(); } @@ -275,7 +261,6 @@ public bool Return(Transaction obj) obj.NetworkWrapper = default; obj.IsServiceTransaction = default; obj.PoolIndex = default; - obj.Initcodes = default; obj._size = default; return true; diff --git a/src/Nethermind/Nethermind.Core/TxType.cs b/src/Nethermind/Nethermind.Core/TxType.cs index c0801626c16..9257754dab6 100644 --- a/src/Nethermind/Nethermind.Core/TxType.cs +++ b/src/Nethermind/Nethermind.Core/TxType.cs @@ -9,7 +9,6 @@ public enum TxType : byte AccessList = 1, EIP1559 = 2, Blob = 3, - EofInitcodeTx = 4, DepositTx = 0x7E, } diff --git a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs index d1e04a3bafd..5b8f597113e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs @@ -42,13 +42,13 @@ public static bool IsValidWithLegacyRules(ReadOnlySpan code) return code is not [InvalidStartingCodeByte, ..]; ; } - public static bool IsValidWithEofRules(ReadOnlySpan code, int fromVersion) + public static bool IsValidWithEofRules(ReadOnlySpan code, int fromVersion, EvmObjectFormat.ValidationStrategy strategy = EvmObjectFormat.ValidationStrategy.Validate) { bool isCodeEof = EvmObjectFormat.IsEof(code, out int codeVersion); bool valid = code.Length >= 1 && codeVersion >= fromVersion && (isCodeEof ? // this needs test cases - EvmObjectFormat.IsValidEof(code, EvmObjectFormat.ValidationStrategy.Validate, out _) : + EvmObjectFormat.IsValidEof(code, strategy, out _) : fromVersion > 0 ? false : IsValidWithLegacyRules(code)); return valid; } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index dfe588013d8..e78ca7645a9 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -9,11 +9,35 @@ namespace Nethermind.Evm.CodeAnalysis; public static class CodeInfoFactory { - public static ICodeInfo CreateCodeInfo(Memory code, IReleaseSpec spec) + public static bool CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy validationRules = EvmObjectFormat.ValidationStrategy.Validate) { - CodeInfo codeInfo = new(code); - return spec.IsEofEnabled && EvmObjectFormat.IsValidEof(code.Span, EvmObjectFormat.ValidationStrategy.Validate, out EofHeader? header) - ? new EofCodeInfo(codeInfo, header.Value) - : codeInfo; + codeinfo = new CodeInfo(code); + if (spec.IsEofEnabled && code.Span.StartsWith(EvmObjectFormat.MAGIC)) + { + if(EvmObjectFormat.IsValidEof(code.Span, validationRules, out EofHeader? header)) + { + codeinfo = new EofCodeInfo(codeinfo, header.Value); + return true; + } + return false; + } + return true; + } + + public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out ICodeInfo codeinfo, out Memory extraCalldata) + { + codeinfo = new CodeInfo(data); + extraCalldata = default; + if(spec.IsEofEnabled && data.Span.StartsWith(EvmObjectFormat.MAGIC)) + { + if(EvmObjectFormat.IsValidEof(data.Span, EvmObjectFormat.ValidationStrategy.ValidateInitcodeMode | EvmObjectFormat.ValidationStrategy.ValidateFullBody | EvmObjectFormat.ValidationStrategy.ValidateSubContainers | EvmObjectFormat.ValidationStrategy.AllowTrailingBytes, out EofHeader? header)) + { + int containerSize = header.Value.DataSection.EndOffset; + extraCalldata = data.Slice(containerSize); + return true; + } + return false; + } + return true; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 48b67ddd52d..a2cedb8af5c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -25,33 +25,33 @@ internal sealed class CodeLruCache { private const int CacheCount = 16; private const int CacheMax = CacheCount - 1; - private readonly LruCacheLowObject[] _caches; + private readonly LruCacheLowObject[] _caches; public CodeLruCache() { - _caches = new LruCacheLowObject[CacheCount]; + _caches = new LruCacheLowObject[CacheCount]; for (int i = 0; i < _caches.Length; i++) { // Cache per nibble to reduce contention as TxPool is very parallel - _caches[i] = new LruCacheLowObject(MemoryAllowance.CodeCacheSize / CacheCount, $"VM bytecodes {i}"); + _caches[i] = new LruCacheLowObject(MemoryAllowance.CodeCacheSize / CacheCount, $"VM bytecodes {i}"); } } - public CodeInfo? Get(in ValueHash256 codeHash) + public ICodeInfo? Get(in ValueHash256 codeHash) { - LruCacheLowObject cache = _caches[GetCacheIndex(codeHash)]; + LruCacheLowObject cache = _caches[GetCacheIndex(codeHash)]; return cache.Get(codeHash); } - public bool Set(in ValueHash256 codeHash, CodeInfo codeInfo) + public bool Set(in ValueHash256 codeHash, ICodeInfo codeInfo) { - LruCacheLowObject cache = _caches[GetCacheIndex(codeHash)]; + LruCacheLowObject cache = _caches[GetCacheIndex(codeHash)]; return cache.Set(codeHash, codeInfo); } private static int GetCacheIndex(in ValueHash256 codeHash) => codeHash.Bytes[^1] & CacheMax; - public bool TryGet(in ValueHash256 codeHash, [NotNullWhen(true)] out CodeInfo? codeInfo) + public bool TryGet(in ValueHash256 codeHash, [NotNullWhen(true)] out ICodeInfo? codeInfo) { codeInfo = Get(codeHash); return codeInfo is not null; @@ -59,47 +59,47 @@ public bool TryGet(in ValueHash256 codeHash, [NotNullWhen(true)] out CodeInfo? c } - private static readonly FrozenDictionary _precompiles = InitializePrecompiledContracts(); + private static readonly FrozenDictionary _precompiles = InitializePrecompiledContracts(); private static readonly CodeLruCache _codeCache = new(); - private static FrozenDictionary InitializePrecompiledContracts() + private static FrozenDictionary InitializePrecompiledContracts() { - return new Dictionary + return new Dictionary { - [EcRecoverPrecompile.Address] = new(EcRecoverPrecompile.Instance), - [Sha256Precompile.Address] = new(Sha256Precompile.Instance), - [Ripemd160Precompile.Address] = new(Ripemd160Precompile.Instance), - [IdentityPrecompile.Address] = new(IdentityPrecompile.Instance), - - [Bn254AddPrecompile.Address] = new(Bn254AddPrecompile.Instance), - [Bn254MulPrecompile.Address] = new(Bn254MulPrecompile.Instance), - [Bn254PairingPrecompile.Address] = new(Bn254PairingPrecompile.Instance), - [ModExpPrecompile.Address] = new(ModExpPrecompile.Instance), - - [Blake2FPrecompile.Address] = new(Blake2FPrecompile.Instance), - - [G1AddPrecompile.Address] = new(G1AddPrecompile.Instance), - [G1MulPrecompile.Address] = new(G1MulPrecompile.Instance), - [G1MultiExpPrecompile.Address] = new(G1MultiExpPrecompile.Instance), - [G2AddPrecompile.Address] = new(G2AddPrecompile.Instance), - [G2MulPrecompile.Address] = new(G2MulPrecompile.Instance), - [G2MultiExpPrecompile.Address] = new(G2MultiExpPrecompile.Instance), - [PairingPrecompile.Address] = new(PairingPrecompile.Instance), - [MapToG1Precompile.Address] = new(MapToG1Precompile.Instance), - [MapToG2Precompile.Address] = new(MapToG2Precompile.Instance), - - [PointEvaluationPrecompile.Address] = new(PointEvaluationPrecompile.Instance), + [EcRecoverPrecompile.Address] = new CodeInfo(EcRecoverPrecompile.Instance), + [Sha256Precompile.Address] = new CodeInfo(Sha256Precompile.Instance), + [Ripemd160Precompile.Address] = new CodeInfo(Ripemd160Precompile.Instance), + [IdentityPrecompile.Address] = new CodeInfo(IdentityPrecompile.Instance), + + [Bn254AddPrecompile.Address] = new CodeInfo(Bn254AddPrecompile.Instance), + [Bn254MulPrecompile.Address] = new CodeInfo(Bn254MulPrecompile.Instance), + [Bn254PairingPrecompile.Address] = new CodeInfo(Bn254PairingPrecompile.Instance), + [ModExpPrecompile.Address] = new CodeInfo(ModExpPrecompile.Instance), + + [Blake2FPrecompile.Address] = new CodeInfo(Blake2FPrecompile.Instance), + + [G1AddPrecompile.Address] = new CodeInfo(G1AddPrecompile.Instance), + [G1MulPrecompile.Address] = new CodeInfo(G1MulPrecompile.Instance), + [G1MultiExpPrecompile.Address] = new CodeInfo(G1MultiExpPrecompile.Instance), + [G2AddPrecompile.Address] = new CodeInfo(G2AddPrecompile.Instance), + [G2MulPrecompile.Address] = new CodeInfo(G2MulPrecompile.Instance), + [G2MultiExpPrecompile.Address] = new CodeInfo(G2MultiExpPrecompile.Instance), + [PairingPrecompile.Address] = new CodeInfo(PairingPrecompile.Instance), + [MapToG1Precompile.Address] = new CodeInfo(MapToG1Precompile.Instance), + [MapToG2Precompile.Address] = new CodeInfo(MapToG2Precompile.Instance), + + [PointEvaluationPrecompile.Address] = new CodeInfo(PointEvaluationPrecompile.Instance), }.ToFrozenDictionary(); } - public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) + public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) { if (codeSource.IsPrecompile(vmSpec)) { return _precompiles[codeSource]; } - CodeInfo? cachedCodeInfo = null; + ICodeInfo? cachedCodeInfo = null; ValueHash256 codeHash = worldState.GetCodeHash(codeSource); if (codeHash == Keccak.OfAnEmptyString.ValueHash256) { @@ -116,8 +116,9 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR MissingCode(codeSource, codeHash); } - cachedCodeInfo = new CodeInfo(code); - cachedCodeInfo.AnalyseInBackgroundIfRequired(); + cachedCodeInfo = CodeInfoFactory.CreateCodeInfo(code, vmSpec); + if(cachedCodeInfo is CodeInfo eof0CodeInfo) + eof0CodeInfo.AnalyseInBackgroundIfRequired(); _codeCache.Set(codeHash, cachedCodeInfo); } else @@ -135,11 +136,11 @@ static void MissingCode(Address codeSource, in ValueHash256 codeHash) } } - public CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode) + public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IReleaseSpec spec) { - if (!_codeCache.TryGet(codeHash, out CodeInfo? codeInfo)) + if (!_codeCache.TryGet(codeHash, out ICodeInfo? codeInfo)) { - codeInfo = new(initCode.ToArray()); + codeInfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec); // Prime the code cache as likely to be used by more txs _codeCache.Set(codeHash, codeInfo); @@ -151,8 +152,9 @@ public CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode) public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - CodeInfo codeInfo = new(code); - codeInfo.AnalyseInBackgroundIfRequired(); + ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec); + if(codeInfo is CodeInfo eof0CodeInfo) + eof0CodeInfo.AnalyseInBackgroundIfRequired(); Hash256 codeHash = code.Length == 0 ? Keccak.OfAnEmptyString : Keccak.Compute(code.Span); state.InsertCode(codeOwner, codeHash, code, spec); diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index 3f17d2904da..84080e493a8 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -11,12 +11,13 @@ namespace Nethermind.Evm.CodeAnalysis; public class EofCodeInfo : ICodeInfo { - private readonly CodeInfo _codeInfo; + private readonly ICodeInfo _codeInfo; private readonly EofHeader _header; public ReadOnlyMemory MachineCode => _codeInfo.MachineCode; public IPrecompile? Precompile => _codeInfo.Precompile; public int Version => _header.Version; + public bool IsEmpty => _codeInfo.IsEmpty; public ReadOnlyMemory TypeSection { get; } public ReadOnlyMemory CodeSection(int index) { @@ -24,6 +25,7 @@ public ReadOnlyMemory CodeSection(int index) return MachineCode.Slice(offset.Start, offset.Size); } public ReadOnlyMemory DataSection { get; } + public ReadOnlyMemory ContainerSection(int index) { var offset = ContainerOffset(index); @@ -51,7 +53,7 @@ _header.ContainerSection is null ); } - public EofCodeInfo(CodeInfo codeInfo, in EofHeader header) + public EofCodeInfo(ICodeInfo codeInfo, in EofHeader header) { _codeInfo = codeInfo; _header = header; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 5f32cc545d2..9a352d4a67c 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using FastEnumUtility; @@ -16,18 +17,37 @@ namespace Nethermind.Evm.EOF; -internal static class EvmObjectFormat +public static class EvmObjectFormat { [StructLayout(LayoutKind.Sequential)] struct Worklet { - public Worklet(ushort position, ushort stackHeight) - { + public Worklet(ushort position, StackBounds stackHeightBounds) + { Position = position; - StackHeight = stackHeight; + StackHeightBounds = stackHeightBounds; } public ushort Position; - public ushort StackHeight; + public StackBounds StackHeightBounds; + } + + [StructLayout(LayoutKind.Sequential)] + struct StackBounds() + { + public short Max = -1; + public short Min = 255; + + public void Combine(StackBounds other) { + this.Max = Math.Max(this.Max, other.Max); + this.Min = Math.Min(this.Min, other.Min); + } + + public bool BoundsEqual() => Max == Min; + + public static bool operator ==(StackBounds left, StackBounds right) => left.Max == right.Max && right.Min == left.Min; + public static bool operator !=(StackBounds left, StackBounds right) => !(left == right); + public override bool Equals(object obj) => obj is StackBounds && this == (StackBounds)obj; + public override int GetHashCode() => Max ^ Min; } public enum ValidationStrategy @@ -35,7 +55,10 @@ public enum ValidationStrategy None = 0, Validate = 1, ValidateSubContainers = Validate | 2, - ValidateFullBody = Validate | 4 + ValidateFullBody = Validate | 4, + ValidateInitcodeMode = Validate | 8, + AllowTrailingBytes = Validate | 16, + } private interface IEofVersionHandler @@ -94,7 +117,7 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s EofHeader h = header.Value; if (handler.ValidateBody(container, h, strategy)) { - if(strategy == ValidationStrategy.ValidateSubContainers && header?.ContainerSection?.Count > 0) + if(strategy.HasFlag(ValidationStrategy.ValidateSubContainers) && header?.ContainerSection?.Count > 0) { int containerSize = header.Value.ContainerSection.Value.Count; @@ -372,7 +395,13 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida return false; } - if (strategy == ValidationStrategy.ValidateFullBody && header.DataSection.Size != dataBody.Length) + if (strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size > dataBody.Length) + { + if (Logger.IsTrace) Logger.Trace("EOF: DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + return false; + } + + if (!strategy.HasFlag(ValidationStrategy.AllowTrailingBytes) && strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size != dataBody.Length) { if (Logger.IsTrace) Logger.Trace("EOF: DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); return false; @@ -412,10 +441,10 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida visitedSections[sectionIdx] = true; (int codeSectionStartOffset, int codeSectionSize) = header.CodeSections[sectionIdx]; - + bool isInitCodeValidationMode = strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode); bool isNonReturning = typesection[sectionIdx * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSectionStartOffset, codeSectionSize); - if (!ValidateInstructions(sectionIdx, isNonReturning, typesection, code, header, container, validationQueue, out ushort jumpsCount)) + if (!ValidateInstructions(sectionIdx, isNonReturning, isInitCodeValidationMode, typesection, code, header, container, validationQueue, out ushort jumpsCount)) { ArrayPool.Shared.Return(visitedSections, true); return false; @@ -469,7 +498,7 @@ bool ValidateTypeSection(ReadOnlySpan types) return true; } - bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist, out ushort jumpsCount) + bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcodeMode, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist, out ushort jumpsCount) { byte[] codeBitmap = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); byte[] jumpdests = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); @@ -483,6 +512,11 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, ReadOnlySpan.Shared.Return(jumpdests, true); } } - public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection, ushort worksetCount) + public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection) { - static Worklet PopWorklet(Worklet[] workset, ref ushort worksetPointer) => workset[worksetPointer++]; - static void PushWorklet(Worklet[] workset, ref ushort worksetTop, Worklet worklet) => workset[worksetTop++] = worklet; + StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); + Array.Fill(recordedStackHeight, new StackBounds()); - short[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); ushort curr_outputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - ushort peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - - ushort worksetTop = 0; ushort worksetPointer = 0; - Worklet[] workset = ArrayPool.Shared.Rent(worksetCount + 1); + short peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; int unreachedBytes = code.Length; + bool isTargetSectionNonReturning = false; - try + int targetMaxStackHeight = 0; + int programCounter = 0; + recordedStackHeight[0].Max = peakStackHeight; + recordedStackHeight[0].Min = peakStackHeight; + StackBounds currentStackBounds = recordedStackHeight[0]; + + while (programCounter < code.Length) { - PushWorklet(workset, ref worksetTop, new Worklet(0, peakStackHeight)); - while (worksetPointer < worksetTop) + Instruction opcode = (Instruction)code[programCounter]; + (ushort? inputs, ushort? outputs, ushort? immediates) = opcode.StackRequirements(); + + ushort posPostInstruction = (ushort)(programCounter + 1 + (immediates ?? 0)); + if (posPostInstruction > code.Length) { - Worklet worklet = PopWorklet(workset, ref worksetPointer); + if (Logger.IsTrace) Logger.Trace($"EOF: PC Reached out of bounds"); + return false; + } - while (worklet.Position < code.Length) - { - Instruction opcode = (Instruction)code[worklet.Position]; - (ushort? inputs, ushort? outputs, ushort? immediates) = opcode.StackRequirements(); - ushort posPostInstruction = (ushort)(worklet.Position + 1); - if (recordedStackHeight[worklet.Position] != 0) + unreachedBytes -= (immediates ?? 0); + + switch (opcode) + { + case Instruction.CALLF or Instruction.JUMPF: + ushort sectionIndex = code.Slice(programCounter + 1, immediates.Value).ReadEthUInt16(); + inputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + + outputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + isTargetSectionNonReturning = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; + outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); + targetMaxStackHeight = typesection.Slice(sectionIndex * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); + + if(MAX_STACK_HEIGHT - targetMaxStackHeight + inputs > currentStackBounds.Max) + { + if (Logger.IsTrace) Logger.Trace($"EOF: stack head during callf must not exceed {MAX_STACK_HEIGHT}"); + return false; + } + + if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && curr_outputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual()) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Stack State invalid, required height {curr_outputs + inputs - outputs} but found {currentStackBounds.Max}"); + return false; + } + break; + case Instruction.DUPN: + byte imm = code[programCounter + 1]; + inputs = (ushort)(imm + 1); + outputs = (ushort)(inputs + 1); + break; + case Instruction.SWAPN: + imm = code[posPostInstruction]; + outputs = inputs = (ushort)(2 + imm); + break; + case Instruction.EXCHANGE: + byte imm_n = (byte)(code[programCounter + 1] >> 4); + byte imm_m = (byte)(code[programCounter + 1] & 0x0F); + outputs = inputs = (ushort)(imm_n + imm_m + 3); + break; + } + + if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputs) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Stack Underflow required {inputs} but found {currentStackBounds.Min}"); + return false; + } + ushort delta = (ushort)(outputs - inputs); + currentStackBounds.Max += (short)(currentStackBounds.Max + delta); + currentStackBounds.Min += (short)(currentStackBounds.Min + delta); + peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); + + switch (opcode) + { + case Instruction.RETF: { - if (worklet.StackHeight != recordedStackHeight[worklet.Position] - 1) + var expectedHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + if (expectedHeight != currentStackBounds.Min || currentStackBounds.BoundsEqual()) { - if (Logger.IsTrace) Logger.Trace($"EOF: Branch joint line has invalid stack height"); + if (Logger.IsTrace) Logger.Trace($"EOF: Stack state invalid required height {expectedHeight} but found {currentStackBounds.Min}"); return false; } break; } - else + case Instruction.RJUMP or Instruction.RJUMPI: { - recordedStackHeight[worklet.Position] = (short)(worklet.StackHeight + 1); - unreachedBytes -= ONE_BYTE_LENGTH; - } + short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); + int jumpDestination = posPostInstruction + offset; - switch (opcode) - { - case Instruction.CALLF or Instruction.JUMPF: - ushort sectionIndex = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); - inputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - - outputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - outputs = (ushort)(outputs == 0x80 ? 0 : outputs); - - ushort maxStackHeight = typesection.Slice(sectionIndex * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); - unreachedBytes -= immediates.Value; + if(opcode is Instruction.RJUMPI) + { + recordedStackHeight[posPostInstruction].Combine(currentStackBounds); + } - if (worklet.StackHeight + maxStackHeight - inputs > MAX_STACK_HEIGHT) + if(jumpDestination > programCounter) + { + recordedStackHeight[jumpDestination].Combine(currentStackBounds); + } else { + if (recordedStackHeight[jumpDestination] != currentStackBounds) { - if (Logger.IsTrace) Logger.Trace($"EOF: stack head during callf must not exceed {MAX_STACK_HEIGHT}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Stack state invalid at {jumpDestination}"); return false; } - break; - case Instruction.DUPN: - byte imm = code[posPostInstruction]; - inputs = (ushort)(imm + 1); - outputs = (ushort)(inputs + 1); - unreachedBytes -= immediates.Value; - break; - case Instruction.SWAPN: - imm = code[posPostInstruction]; - outputs = inputs = (ushort)(1 + imm); - unreachedBytes -= immediates.Value; - break; - case Instruction.EXCHANGE: - byte imm_n = (byte)(code[posPostInstruction] >> 4); - byte imm_m = (byte)(code[posPostInstruction] & 0x0F); - outputs = inputs = (ushort)(imm_n + imm_m); - unreachedBytes -= immediates.Value; - break; - } + } - if (worklet.StackHeight < inputs) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Stack Underflow required {inputs} but found {worklet.StackHeight}"); - return false; + unreachedBytes -= immediates.Value; + break; } - - worklet.StackHeight += (ushort)(outputs - inputs + (opcode is Instruction.JUMPF ? curr_outputs : 0)); - peakStackHeight = Math.Max(peakStackHeight, worklet.StackHeight); - - switch (opcode) + case Instruction.RJUMPV: { - case Instruction.JUMPF: - { - if (curr_outputs < outputs) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Output Count {outputs} must be less or equal than sectionId {sectionId} output count {curr_outputs}"); - return false; - } - - if (worklet.StackHeight != curr_outputs + inputs - outputs) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Stack Height must {curr_outputs + inputs - outputs} but found {worklet.StackHeight}"); - return false; - } - - break; - } - case Instruction.RJUMP or Instruction.RJUMPI: + var count = code[posPostInstruction] + 1; + immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); + for (short j = 0; j < count; j++) + { + int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; + int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); + int jumpDestination = posPostInstruction + immediates.Value + offset; + if (jumpDestination > programCounter) { - short offset = code.Slice(posPostInstruction, immediates.Value).ReadEthInt16(); - int jumpDestination = posPostInstruction + immediates.Value + offset; - PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); - posPostInstruction += immediates.Value; - unreachedBytes -= immediates.Value; - break; + recordedStackHeight[jumpDestination].Combine(currentStackBounds); } - case Instruction.RJUMPV: + else { - var count = code[posPostInstruction] + 1; - immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); - for (short j = 0; j < count; j++) + if (recordedStackHeight[jumpDestination] != currentStackBounds) { - int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; - int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); - int jumpDestination = posPostInstruction + immediates.Value + offset; - PushWorklet(workset, ref worksetTop, new Worklet((ushort)jumpDestination, worklet.StackHeight)); + if (Logger.IsTrace) Logger.Trace($"EOF: Stack state invalid at {jumpDestination}"); + return false; } - posPostInstruction += immediates.Value; - unreachedBytes -= immediates.Value; - break; } - default: - { - unreachedBytes -= immediates.Value; - posPostInstruction += immediates.Value; - break; - } - } - - worklet.Position = posPostInstruction; - if (opcode is Instruction.RJUMP) - { - break; - } + } - if (opcode.IsTerminating()) - { - var expectedHeight = opcode is Instruction.RETF ? typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] : worklet.StackHeight; - if (expectedHeight != worklet.StackHeight) + posPostInstruction += immediates.Value; + if(posPostInstruction > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Stack state invalid required height {expectedHeight} but found {worklet.StackHeight}"); + if (Logger.IsTrace) Logger.Trace($"EOF: PC Reached out of bounds"); return false; } + unreachedBytes -= immediates.Value; break; } - - else if (worklet.Position >= code.Length) + default: { - if (Logger.IsTrace) Logger.Trace($"EOF: Invalid code, reached end of code without a terminating instruction"); - return false; + unreachedBytes -= immediates.Value; + posPostInstruction += immediates.Value; + break; } - } } - if (unreachedBytes > 0) - { - if (Logger.IsTrace) Logger.Trace($"EOF: bytecode has unreachable segments"); - return false; - } + programCounter = posPostInstruction; - if (peakStackHeight != suggestedMaxHeight) + if (opcode.IsTerminating()) { - if (Logger.IsTrace) Logger.Trace($"EOF: Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); - return false; - } - - bool result = peakStackHeight <= MAX_STACK_HEIGHT; - if (!result) + currentStackBounds = recordedStackHeight[programCounter]; + } else { - if (Logger.IsTrace) Logger.Trace($"EOF: stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); - return false; + currentStackBounds.Combine(recordedStackHeight[programCounter]); + recordedStackHeight[programCounter] = currentStackBounds; } - return result; } - finally + + if (unreachedBytes != 0) { - ArrayPool.Shared.Return(recordedStackHeight, true); - ArrayPool.Shared.Return(workset, true); + if (Logger.IsTrace) Logger.Trace($"EOF: bytecode has unreachable segments"); + return false; + } + + if (peakStackHeight != suggestedMaxHeight) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); + return false; + } + + bool result = peakStackHeight <= MAX_STACK_HEIGHT; + if (!result) + { + if (Logger.IsTrace) Logger.Trace($"EOF: stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); + return false; } + return result; } } } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index 05d2523b7a9..43b30c4b99a 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -11,6 +11,7 @@ namespace Nethermind.Evm.CodeAnalysis; public interface ICodeInfo { int Version => 0; + bool IsEmpty { get; } ReadOnlyMemory MachineCode { get; } IPrecompile? Precompile { get; } bool IsPrecompile => Precompile is not null; diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs index 6fde3cbfe7f..c562777ed34 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs @@ -12,7 +12,7 @@ namespace Nethermind.Evm; public interface ICodeInfoRepository { - CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec); - CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode); + ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec); + ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IReleaseSpec vmSpec); void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs index e716b01948d..b82f068b561 100644 --- a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs +++ b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs @@ -24,7 +24,6 @@ public static long Calculate(Transaction transaction, IReleaseSpec releaseSpec) result += DataCost(transaction, releaseSpec); result += CreateCost(transaction, releaseSpec); result += AccessListCost(transaction, releaseSpec); - result += EofInitCodeCost(transaction, releaseSpec); return result; } @@ -39,24 +38,6 @@ private static long CreateCost(Transaction transaction, IReleaseSpec releaseSpec return createCost; } - private static long EofInitCodeCost(Transaction transaction, IReleaseSpec releaseSpec) - { - if(releaseSpec.IsEofEnabled && transaction.IsEofContractCreation) - { - long txDataNonZeroGasCost = - releaseSpec.IsEip2028Enabled ? GasCostOf.TxDataNonZeroEip2028 : GasCostOf.TxDataNonZero; - - long initcodeCosts = 0; - foreach(var initcode in transaction.Initcodes) - { - int totalZeros = initcode.AsSpan().CountZeros(); - initcodeCosts += totalZeros * GasCostOf.TxDataZero + (initcode.Length - totalZeros) * txDataNonZeroGasCost; - } - return initcodeCosts; - } - return 0; - } - private static long DataCost(Transaction transaction, IReleaseSpec releaseSpec) { Span data = transaction.Data.GetValueOrDefault().Span; diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 88be62ec6bc..5deb3465a20 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -12,12 +12,14 @@ using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Evm.CodeAnalysis; +using Nethermind.Evm.EOF; using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Specs; using Nethermind.State; using Nethermind.State.Tracing; +using Org.BouncyCastle.Bcpg; using static Nethermind.Core.Extensions.MemoryExtensions; using static Nethermind.Evm.VirtualMachine; @@ -130,10 +132,12 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon if (commit) WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullTxTracer.Instance, commitStorageRoots: false); - ExecutionEnvironment env = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice); - long gasAvailable = tx.GasLimit - intrinsicGas; - ExecuteEvmCall(tx, header, spec, tracer, opts, gasAvailable, env, out TransactionSubstate? substate, out long spentGas, out byte statusCode); + if(!(result = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, out ExecutionEnvironment? env))) { + return result; + } + + ExecuteEvmCall(tx, header, spec, tracer, opts, gasAvailable, env.Value, out TransactionSubstate? substate, out long spentGas, out byte statusCode); PayFees(tx, header, spec, tracer, substate, spentGas, premiumPerGas, statusCode); // Finalize @@ -171,12 +175,12 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon if (statusCode == StatusCode.Failure) { byte[] output = (substate?.ShouldRevert ?? false) ? substate.Output.ToArray() : Array.Empty(); - tracer.MarkAsFailed(env.ExecutingAccount, spentGas, output, substate?.Error, stateRoot); + tracer.MarkAsFailed(env.Value.ExecutingAccount, spentGas, output, substate?.Error, stateRoot); } else { LogEntry[] logs = substate.Logs.Count != 0 ? substate.Logs.ToArray() : Array.Empty(); - tracer.MarkAsSuccess(env.ExecutingAccount, spentGas, substate.Output.ToArray(), logs, stateRoot); + tracer.MarkAsSuccess(env.Value.ExecutingAccount, spentGas, substate.Output.ToArray(), logs, stateRoot); } } @@ -399,26 +403,36 @@ protected virtual TransactionResult IncrementNonce(Transaction tx, BlockHeader h return TransactionResult.Ok; } - protected ExecutionEnvironment BuildExecutionEnvironment( + protected TransactionResult BuildExecutionEnvironment( Transaction tx, in BlockExecutionContext blCtx, IReleaseSpec spec, - in UInt256 effectiveGasPrice) + in UInt256 effectiveGasPrice, + out ExecutionEnvironment? env) { Address recipient = tx.GetRecipient(tx.IsContractCreation ? WorldState.GetNonce(tx.SenderAddress) : 0); if (recipient is null) ThrowInvalidDataException("Recipient has not been resolved properly before tx execution"); - TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, tx.Initcodes); + TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes); - CodeInfo codeInfo = tx.IsContractCreation - ? new(tx.Data ?? Memory.Empty) - : _codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec); - - codeInfo.AnalyseInBackgroundIfRequired(); + env = null; + ICodeInfo codeInfo = null; byte[] inputData = tx.IsMessageCall ? tx.Data.AsArray() ?? Array.Empty() : Array.Empty(); + if (tx.IsContractCreation) + { + if(CodeInfoFactory.CreateInitCodeInfo(tx.Data ?? default, spec, out codeInfo, out Memory trailingData)) { + inputData = trailingData.ToArray(); + } else { + return "Eip 7698: Invalid CreateTx Initcode"; + } + } else + { + codeInfo = _codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec); + (codeInfo as CodeInfo).AnalyseInBackgroundIfRequired(); + } - return new ExecutionEnvironment + env = new ExecutionEnvironment ( txExecutionContext: in executionContext, value: tx.Value, @@ -429,6 +443,7 @@ protected ExecutionEnvironment BuildExecutionEnvironment( inputData: inputData, codeInfo: codeInfo ); + return TransactionResult.Ok; } protected void ExecuteEvmCall( diff --git a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs index 6be5f9fb2da..02ab0348d0f 100644 --- a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs +++ b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs @@ -12,15 +12,12 @@ public readonly struct TxExecutionContext public Address Origin { get; } public UInt256 GasPrice { get; } public byte[][]? BlobVersionedHashes { get; } - public byte[][]? InitCodes { get; } - - public TxExecutionContext(in BlockExecutionContext blockExecutionContext, Address origin, in UInt256 gasPrice, byte[][] blobVersionedHashes, byte[][] initicodes) + public TxExecutionContext(in BlockExecutionContext blockExecutionContext, Address origin, in UInt256 gasPrice, byte[][] blobVersionedHashes) { BlockExecutionContext = blockExecutionContext; Origin = origin; GasPrice = gasPrice; BlobVersionedHashes = blobVersionedHashes; - InitCodes = initicodes; } } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3a1d249e1e5..f5fcb372f3a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2217,7 +2217,7 @@ private CallResult ExecuteCode EvmObjectFormat.Eof1.MAX_STACK_HEIGHT) + if (EvmObjectFormat.Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) { goto StackOverflow; } @@ -2248,14 +2248,11 @@ private CallResult ExecuteCode EvmObjectFormat.Eof1.MAX_STACK_HEIGHT) + if (EvmObjectFormat.Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) { goto StackOverflow; } - if (vmState.ReturnStackHead + 1 == EvmObjectFormat.Eof1.RETURN_STACK_MAX_HEIGHT) - goto InvalidSubroutineEntry; - sectionIndex = index; (programCounter, _) = env.CodeInfo.SectionOffset(index); break; @@ -2540,8 +2537,9 @@ private EvmExceptionType InstructionCall( !UpdateMemoryCost(vmState, ref gasAvailable, in outputOffset, outputLength) || !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; - CodeInfo codeInfo = _codeInfoRepository.GetCachedCodeInfo(_worldState, codeSource, spec); - codeInfo.AnalyseInBackgroundIfRequired(); + ICodeInfo codeInfo = _codeInfoRepository.GetCachedCodeInfo(_worldState, codeSource, spec); + if(codeInfo is CodeInfo eof0CodeInfo) + eof0CodeInfo.AnalyseInBackgroundIfRequired(); if (spec.Use63Over64Rule) { diff --git a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs index 2369750e942..ee5d7a54740 100644 --- a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs +++ b/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs @@ -132,8 +132,6 @@ public TransactionForRpc() { } public TxType Type { get; set; } - public byte[][] Initcodes { get; set;} - public IEnumerable? AccessList { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -182,11 +180,6 @@ public TransactionForRpc() { } tx.BlobVersionedHashes = BlobVersionedHashes; } - if(tx.SupportsEofInitcode) - { - tx.Initcodes = Initcodes; - } - return tx; } @@ -227,11 +220,6 @@ public TransactionForRpc() { } tx.BlobVersionedHashes = BlobVersionedHashes; } - if (tx.SupportsEofInitcode) - { - tx.Initcodes = Initcodes; - } - return tx; } diff --git a/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs b/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs index dbe43ddddc7..83c01820acb 100644 --- a/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs @@ -14,14 +14,14 @@ namespace Nethermind.Facade; public class OverridableCodeInfoRepository(ICodeInfoRepository codeInfoRepository) : ICodeInfoRepository { - private readonly Dictionary _codeOverwrites = new(); + private readonly Dictionary _codeOverwrites = new(); - public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) => - _codeOverwrites.TryGetValue(codeSource, out CodeInfo result) + public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) => + _codeOverwrites.TryGetValue(codeSource, out ICodeInfo result) ? result : codeInfoRepository.GetCachedCodeInfo(worldState, codeSource, vmSpec); - public CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode) => codeInfoRepository.GetOrAdd(codeHash, initCode); + public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IReleaseSpec spec) => codeInfoRepository.GetOrAdd(codeHash, initCode, spec); public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) => codeInfoRepository.InsertCode(state, code, codeOwner, spec); @@ -31,7 +31,7 @@ public void SetCodeOverwrite( IWorldState worldState, IReleaseSpec vmSpec, Address key, - CodeInfo value, + ICodeInfo value, Address? redirectAddress = null) { if (redirectAddress is not null) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs index 63f9efafca4..12548d15df7 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs @@ -90,9 +90,6 @@ protected virtual T NewTx() case TxType.Blob: DecodeShardBlobPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); break; - case TxType.EofInitcodeTx: - DecodeEofPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); - break; case TxType.DepositTx: TxDecoder.DecodeDepositPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); break; @@ -229,7 +226,6 @@ private void DecodeEofPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpB transaction.Value = rlpStream.DecodeUInt256(); transaction.Data = rlpStream.DecodeByteArray(); transaction.AccessList = _accessListDecoder.Decode(rlpStream, rlpBehaviors); - transaction.Initcodes = rlpStream.DecodeByteArrays(); } private void DecodeShardBlobPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) @@ -312,7 +308,6 @@ private void DecodeEofPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderConte transaction.Value = decoderContext.DecodeUInt256(); transaction.Data = decoderContext.DecodeByteArrayMemory(); transaction.AccessList = _accessListDecoder.Decode(ref decoderContext, rlpBehaviors); - transaction.Initcodes = decoderContext.DecodeByteArrays(); } @@ -386,20 +381,6 @@ private void EncodeEip1559PayloadWithoutPayload(T item, RlpStream stream, RlpBeh _accessListDecoder.Encode(stream, item.AccessList, rlpBehaviors); } - private void EncodeEofPayloadWithoutPayload(T item, RlpStream stream, RlpBehaviors rlpBehaviors) - { - stream.Encode(item.ChainId ?? 0); - stream.Encode(item.Nonce); - stream.Encode(item.GasPrice); // gas premium - stream.Encode(item.DecodedMaxFeePerGas); - stream.Encode(item.GasLimit); - stream.Encode(item.To); - stream.Encode(item.Value); - stream.Encode(item.Data); - _accessListDecoder.Encode(stream, item.AccessList, rlpBehaviors); - stream.Encode(item.Initcodes); - } - private void EncodeShardBlobPayloadWithoutPayload(T item, RlpStream stream, RlpBehaviors rlpBehaviors) { stream.Encode(item.ChainId ?? 0); @@ -503,9 +484,6 @@ public void Decode(ref Rlp.ValueDecoderContext decoderContext, ref T? transactio case TxType.EIP1559: DecodeEip1559PayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); break; - case TxType.EofInitcodeTx: - DecodeEofPayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); - break; case TxType.Blob: DecodeShardBlobPayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); break; @@ -707,9 +685,6 @@ private void EncodeTx(RlpStream stream, T? item, RlpBehaviors rlpBehaviors = Rlp case TxType.Blob: EncodeShardBlobPayloadWithoutPayload(item, stream, rlpBehaviors); break; - case TxType.EofInitcodeTx: - EncodeEofPayloadWithoutPayload(item, stream, rlpBehaviors); - break; case TxType.DepositTx: TxDecoder.EncodeDepositTxPayloadWithoutPayload(item, stream); break; @@ -813,8 +788,7 @@ private int GetEofContentLength(T item) + Rlp.LengthOf(item.Value) + Rlp.LengthOf(item.Data) + Rlp.LengthOf(item.ChainId ?? 0) - + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None) - + Rlp.LengthOf(item.Initcodes); + + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None); } private int GetShardBlobContentLength(T item) @@ -872,9 +846,6 @@ private int GetContentLength(T item, bool forSigning, bool isEip155Enabled = fal case TxType.Blob: contentLength = GetShardBlobContentLength(item); break; - case TxType.EofInitcodeTx: - contentLength = GetEofContentLength(item); - break; case TxType.DepositTx: contentLength = TxDecoder.GetDepositTxContentLength(item); break; From 62caacb2b73a544518f79f379247da1886e6c572 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 17 Jun 2024 12:57:33 +0100 Subject: [PATCH 052/255] fixed build issues --- src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs | 6 +++--- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 12 ++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index a2cedb8af5c..de4dc0732c8 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -116,7 +116,7 @@ public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, I MissingCode(codeSource, codeHash); } - cachedCodeInfo = CodeInfoFactory.CreateCodeInfo(code, vmSpec); + CodeInfoFactory.CreateCodeInfo(code, vmSpec, out cachedCodeInfo, EOF.EvmObjectFormat.ValidationStrategy.None); if(cachedCodeInfo is CodeInfo eof0CodeInfo) eof0CodeInfo.AnalyseInBackgroundIfRequired(); _codeCache.Set(codeHash, cachedCodeInfo); @@ -140,7 +140,7 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR { if (!_codeCache.TryGet(codeHash, out ICodeInfo? codeInfo)) { - codeInfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec); + CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out codeInfo, EOF.EvmObjectFormat.ValidationStrategy.None); // Prime the code cache as likely to be used by more txs _codeCache.Set(codeHash, codeInfo); @@ -152,7 +152,7 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec); + CodeInfoFactory.CreateCodeInfo(code, spec, out ICodeInfo codeInfo, EOF.EvmObjectFormat.ValidationStrategy.None); if(codeInfo is CodeInfo eof0CodeInfo) eof0CodeInfo.AnalyseInBackgroundIfRequired(); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f5fcb372f3a..62462dbc5b8 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2896,7 +2896,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref { ref readonly ExecutionEnvironment env = ref vmState.Env; - var currentContext = instruction == Instruction.EOFCREATE ? ExecutionType.EOFCREATE: ExecutionType.TXCREATE; + var currentContext = ExecutionType.EOFCREATE; if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); @@ -2990,7 +2990,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec); + CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.None); ExecutionEnvironment callEnv = new ( @@ -3007,11 +3007,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref EvmState callState = new( callGas, callEnv, - instruction switch - { - Instruction.EOFCREATE => ExecutionType.EOFCREATE, - _ => throw new UnreachableException() - }, + currentContext, false, snapshot, 0L, @@ -3143,7 +3139,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid // for another tx as returned to pool. - ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec); + CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.None); if(codeinfo is CodeInfo classicalCode) { classicalCode.AnalyseInBackgroundIfRequired(); From dfbd13d10f5e85438fca2102224dbe960e413cf1 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 20 Jun 2024 00:39:10 +0100 Subject: [PATCH 053/255] fix build issues --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 4 +-- .../Nethermind.Evm/CodeInfoFactory.cs | 2 +- .../Nethermind.Evm/CodeInfoRepository.cs | 15 +++++----- .../EvmObjectFormat/EofCodeInfo.cs | 15 ++++------ .../EvmObjectFormat/EofCodeValidator.cs | 12 ++++---- .../EvmObjectFormat/EofHeader.cs | 28 ++++++------------- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 6 ++-- .../Nethermind.Evm/VirtualMachine.cs | 10 +++---- .../ChainSpecStyle/ChainParameters.cs | 1 + .../ChainSpecBasedSpecProvider.cs | 2 +- .../ChainSpecStyle/ChainSpecLoader.cs | 1 + .../Json/ChainSpecParamsJson.cs | 1 + 12 files changed, 44 insertions(+), 53 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 3d901b0e69c..f7aa87037c5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -58,12 +58,12 @@ public void AnalyseInBackgroundIfRequired() } } - public SectionHeader SectionOffset(int idx) + public SectionHeader CodeSectionOffset(int idx) { throw new NotImplementedException(); } - public SectionHeader? ContainerOffset(int idx) + public SectionHeader? ContainerSectionOffset(int idx) { throw new NotImplementedException(); } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index e78ca7645a9..cecf57f0156 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -9,7 +9,7 @@ namespace Nethermind.Evm.CodeAnalysis; public static class CodeInfoFactory { - public static bool CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy validationRules = EvmObjectFormat.ValidationStrategy.Validate) + public static bool CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy validationRules = EvmObjectFormat.ValidationStrategy.ExractHeader) { codeinfo = new CodeInfo(code); if (spec.IsEofEnabled && code.Span.StartsWith(EvmObjectFormat.MAGIC)) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 27a2a81443d..218eecbd7d0 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -62,7 +62,7 @@ public bool TryGet(in ValueHash256 codeHash, [NotNullWhen(true)] out ICodeInfo? private static readonly FrozenDictionary _precompiles = InitializePrecompiledContracts(); private static readonly CodeLruCache _codeCache = new(); - private readonly FrozenDictionary _localPrecompiles; + private readonly FrozenDictionary _localPrecompiles; private static FrozenDictionary InitializePrecompiledContracts() { @@ -103,7 +103,6 @@ public CodeInfoRepository(ConcurrentDictionary kvp.Key, kvp => CreateCachedPrecompile(kvp, precompileCache)); } - public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) { if (codeSource.IsPrecompile(vmSpec)) @@ -128,7 +127,7 @@ public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, I MissingCode(codeSource, codeHash); } - CodeInfoFactory.CreateCodeInfo(code, vmSpec, out cachedCodeInfo, EOF.EvmObjectFormat.ValidationStrategy.None); + CodeInfoFactory.CreateCodeInfo(code, vmSpec, out cachedCodeInfo, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); if(cachedCodeInfo is CodeInfo eof0CodeInfo) eof0CodeInfo.AnalyseInBackgroundIfRequired(); _codeCache.Set(codeHash, cachedCodeInfo); @@ -152,7 +151,7 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR { if (!_codeCache.TryGet(codeHash, out ICodeInfo? codeInfo)) { - CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out codeInfo, EOF.EvmObjectFormat.ValidationStrategy.None); + CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out codeInfo, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); // Prime the code cache as likely to be used by more txs _codeCache.Set(codeHash, codeInfo); @@ -164,7 +163,7 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - CodeInfoFactory.CreateCodeInfo(code, spec, out ICodeInfo codeInfo, EOF.EvmObjectFormat.ValidationStrategy.None); + CodeInfoFactory.CreateCodeInfo(code, spec, out ICodeInfo codeInfo, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); if(codeInfo is CodeInfo eof0CodeInfo) eof0CodeInfo.AnalyseInBackgroundIfRequired(); @@ -173,10 +172,10 @@ public void InsertCode(IWorldState state, ReadOnlyMemory code, Address cod _codeCache.Set(codeHash, codeInfo); } - private CodeInfo CreateCachedPrecompile( - in KeyValuePair originalPrecompile, + private ICodeInfo CreateCachedPrecompile( + in KeyValuePair originalPrecompile, ConcurrentDictionary, bool)> cache) => - new(new CachedPrecompile(originalPrecompile.Key.Value, originalPrecompile.Value.Precompile!, cache)); + new CodeInfo(new CachedPrecompile(originalPrecompile.Key.Value, originalPrecompile.Value.Precompile!, cache)); private class CachedPrecompile( Address address, diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index 84080e493a8..eb1ab16fc91 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -19,25 +19,21 @@ public class EofCodeInfo : ICodeInfo public int Version => _header.Version; public bool IsEmpty => _codeInfo.IsEmpty; public ReadOnlyMemory TypeSection { get; } - public ReadOnlyMemory CodeSection(int index) - { - var offset = SectionOffset(index); - return MachineCode.Slice(offset.Start, offset.Size); - } + public ReadOnlyMemory CodeSection { get; } public ReadOnlyMemory DataSection { get; } public ReadOnlyMemory ContainerSection(int index) { - var offset = ContainerOffset(index); + var offset = ContainerSectionOffset(index); if (offset is null) return Memory.Empty; else { - return MachineCode.Slice(offset.Value.Start, offset.Value.Size); + return MachineCode.Slice(_header.ContainerSection.Value.Start + offset.Value.Start, offset.Value.Size); } } - public SectionHeader SectionOffset(int sectionId) => _header.CodeSections[sectionId]; - public SectionHeader? ContainerOffset(int containerId) => + public SectionHeader CodeSectionOffset(int sectionId) => _header.CodeSections[sectionId]; + public SectionHeader? ContainerSectionOffset(int containerId) => _header.ContainerSection is null ? null : _header.ContainerSection.Value[containerId]; @@ -59,5 +55,6 @@ public EofCodeInfo(ICodeInfo codeInfo, in EofHeader header) _header = header; TypeSection = MachineCode.Slice(_header.TypeSection.Start, _header.TypeSection.Size); DataSection = MachineCode.Slice(_header.DataSection.Start, _header.DataSection.Size); + CodeSection = MachineCode.Slice(_header.CodeSections.Start, _header.CodeSections.Size); } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 9a352d4a67c..ab125117f6a 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -58,6 +58,7 @@ public enum ValidationStrategy ValidateFullBody = Validate | 4, ValidateInitcodeMode = Validate | 8, AllowTrailingBytes = Validate | 16, + ExractHeader = 32, } @@ -114,17 +115,17 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) && handler.TryParseEofHeader(container, out header)) { - EofHeader h = header.Value; - if (handler.ValidateBody(container, h, strategy)) + bool validateBody = strategy.HasFlag(ValidationStrategy.Validate); + if (validateBody && handler.ValidateBody(container, header.Value, strategy)) { - if(strategy.HasFlag(ValidationStrategy.ValidateSubContainers) && header?.ContainerSection?.Count > 0) + if (strategy.HasFlag(ValidationStrategy.ValidateSubContainers) && header?.ContainerSection?.Count > 0) { int containerSize = header.Value.ContainerSection.Value.Count; for (int i = 0; i < containerSize; i++) { ReadOnlySpan subContainer = container.Slice(header.Value.ContainerSection.Value.Start + header.Value.ContainerSection.Value[i].Start, header.Value.ContainerSection.Value[i].Size); - if(!IsValidEof(subContainer, strategy, out _)) + if (!IsValidEof(subContainer, strategy, out _)) { return false; } @@ -133,6 +134,7 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s } return true; } + return !validateBody; } header = null; @@ -198,7 +200,7 @@ internal enum Separator : byte internal const byte MAX_STACK_HEIGHT_OFFSET = OUTPUTS_OFFSET + 1; internal const int MAX_STACK_HEIGHT_LENGTH = 2; - internal const ushort MAX_STACK_HEIGHT = 0x3FF; + internal const ushort MAX_STACK_HEIGHT = 0x400; internal const ushort MINIMUM_NUM_CODE_SECTIONS = 1; internal const ushort MAXIMUM_NUM_CODE_SECTIONS = 1024; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index 33a039404b5..c3e13c2f60e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -18,44 +18,34 @@ public struct EofHeader() public required CompoundSectionHeader? ContainerSection; } -public readonly record struct SectionHeader(int Start, ushort Size) +public record struct SectionHeader(int Start, ushort Size) { public int EndOffset => Start + Size; } -public readonly record struct CompoundSectionHeader(int Start, int[] SubSectionsSizes) +public record struct CompoundSectionHeader(int Start, int[] SubSectionsSizes) { public readonly int EndOffset = Start + SubSectionsSizes.Sum(); public int Size => EndOffset - Start; public int Count => SubSectionsSizes.Length; - /* - private readonly int[] subSectionsSizesAcc; - private readonly int[] SubSectionsSizesAcc + private int[] subSectionsSizesAcc; + private int[] SubSectionsSizesAcc { - init + get { if(subSectionsSizesAcc is null) { subSectionsSizesAcc = new int[SubSectionsSizes.Length]; - } - - for (var i = 0; i < SubSectionsSizes.Length; i++) - { - if(i == 0) - { - subSectionsSizesAcc[i] = 0; - } else + subSectionsSizesAcc[0] = 0; + for (var i = 1; i < SubSectionsSizes.Length; i++) { subSectionsSizesAcc[i] = subSectionsSizesAcc[i - 1] + SubSectionsSizes[i]; } } - } - get => subSectionsSizesAcc; + return subSectionsSizesAcc; + } } public SectionHeader this[int i] => new SectionHeader(Start: SubSectionsSizesAcc[i], Size: (ushort)SubSectionsSizes[i]); - */ - // returns a subsection with localized indexing [0, size] ==> 0 is local to the section not the entire bytecode - public SectionHeader this[int i] => new SectionHeader(Start: SubSectionsSizes[..i].Sum(), Size: (ushort)SubSectionsSizes[i]); } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index 43b30c4b99a..de3643d2d43 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -16,10 +16,10 @@ public interface ICodeInfo IPrecompile? Precompile { get; } bool IsPrecompile => Precompile is not null; ReadOnlyMemory TypeSection => Memory.Empty; - ReadOnlyMemory CodeSection(int _) => MachineCode; + ReadOnlyMemory CodeSection => MachineCode; ReadOnlyMemory DataSection => Memory.Empty; ReadOnlyMemory ContainerSection(int _) => Memory.Empty; - SectionHeader SectionOffset(int idx); - SectionHeader? ContainerOffset(int idx); + SectionHeader CodeSectionOffset(int idx); + SectionHeader? ContainerSectionOffset(int idx); (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 62462dbc5b8..a7bd3a5dde0 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -783,7 +783,7 @@ private CallResult ExecuteCode codeSection = env.CodeInfo.CodeSection(0).Span; + ReadOnlySpan codeSection = env.CodeInfo.CodeSection.Span; ReadOnlySpan dataSection = env.CodeInfo.DataSection.Span; EvmExceptionType exceptionType = EvmExceptionType.None; @@ -2233,7 +2233,7 @@ private CallResult ExecuteCode(EvmState vmState, ref _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.None); + CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.ExractHeader); ExecutionEnvironment callEnv = new ( @@ -3139,7 +3139,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid // for another tx as returned to pool. - CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.None); + CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.ExractHeader); if(codeinfo is CodeInfo classicalCode) { classicalCode.AnalyseInBackgroundIfRequired(); diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs index d3f5c970570..162b1f84abc 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs @@ -120,6 +120,7 @@ public class ChainParameters public ulong? Eip2935TransitionTimestamp { get; set; } public Address Eip2935ContractAddress { get; set; } public ulong? Rip7212TransitionTimestamp { get; set; } + public ulong? Eip7692TransitionTimestamp { get; set; } #region EIP-4844 parameters /// diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index d0b3805a52f..9d679338850 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -205,7 +205,6 @@ private static ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releaseSt releaseSpec.IsEip3607Enabled = (chainSpec.Parameters.Eip3607Transition ?? long.MaxValue) <= releaseStartBlock; releaseSpec.ValidateChainId = (chainSpec.Parameters.ValidateChainIdTransition ?? 0) <= releaseStartBlock; releaseSpec.ValidateReceipts = ((chainSpec.Parameters.ValidateReceiptsTransition > 0) ? Math.Max(chainSpec.Parameters.ValidateReceiptsTransition ?? 0, chainSpec.Parameters.Eip658Transition ?? 0) : 0) <= releaseStartBlock; - releaseSpec.Eip1559FeeCollector = releaseSpec.IsEip1559Enabled && (chainSpec.Parameters.Eip1559FeeCollectorTransition ?? long.MaxValue) <= releaseStartBlock ? chainSpec.Parameters.Eip1559FeeCollector : null; releaseSpec.Eip1559BaseFeeMinValue = releaseSpec.IsEip1559Enabled && (chainSpec.Parameters.Eip1559BaseFeeMinValueTransition ?? long.MaxValue) <= releaseStartBlock ? chainSpec.Parameters.Eip1559BaseFeeMinValue : null; releaseSpec.ElasticityMultiplier = chainSpec.Parameters.Eip1559ElasticityMultiplier ?? Eip1559Constants.DefaultElasticityMultiplier; @@ -252,6 +251,7 @@ private static ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releaseSt 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; return releaseSpec; diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index 068c4d19365..93fe9955f88 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -142,6 +142,7 @@ bool GetForInnerPathExistence(KeyValuePair o) => Eip5656TransitionTimestamp = chainSpecJson.Params.Eip5656TransitionTimestamp, Eip6780TransitionTimestamp = chainSpecJson.Params.Eip6780TransitionTimestamp, Rip7212TransitionTimestamp = chainSpecJson.Params.Rip7212TransitionTimestamp, + Eip7692TransitionTimestamp = chainSpecJson.Params.Eip7692TransitionTimestamp, Eip4788TransitionTimestamp = chainSpecJson.Params.Eip4788TransitionTimestamp, Eip4788ContractAddress = chainSpecJson.Params.Eip4788ContractAddress ?? Eip4788Constants.BeaconRootsAddress, Eip2935TransitionTimestamp = chainSpecJson.Params.Eip2935TransitionTimestamp, diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs index 499022ca0c6..9e7b4a7c90b 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs @@ -148,4 +148,5 @@ internal class ChainSpecParamsJson public UInt256? Eip4844MinBlobGasPrice { get; set; } public ulong? Eip4844TargetBlobGasPerBlock { get; set; } public ulong? Rip7212TransitionTimestamp { get; set; } + public ulong? Eip7692TransitionTimestamp { get; set; } } From be7fc223f528b82a537eb2e632370ce10633832e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 20 Jun 2024 23:42:33 +0100 Subject: [PATCH 054/255] fix build issue --- src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs | 2 +- .../Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs | 2 +- src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index 17550d812d0..e854d7f2285 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -55,7 +55,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(ByteCode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, []), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, []), inputData: default ); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs index aea6c03c82f..83212465d32 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs @@ -87,7 +87,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(_bytecode.Concat(_bytecode).Concat(_bytecode).Concat(_bytecode).ToArray()), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, []), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, []), inputData: default ); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs index de350735e55..6efb4aa91a5 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs @@ -98,7 +98,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(Bytecode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, []), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, []), inputData: default ); From f4da9ec29d2bd7ce28ef500ce25263b613c8ac52 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 21 Jun 2024 13:46:12 +0100 Subject: [PATCH 055/255] added version to EOF logs --- .../EvmObjectFormat/EofCodeValidator.cs | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index ab125117f6a..f30e9ca5bae 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -229,13 +229,13 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => if (!container.StartsWith(MAGIC)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Code doesn't start with Magic byte sequence expected {MAGIC.ToHexString(true)} "); + 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: Code is not Eof version {VERSION}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not Eof version {VERSION}"); return false; } @@ -245,13 +245,13 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => int TYPESECTION_HEADER_ENDOFFSET = TYPESECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; if (container[TYPESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_TYPE) { - if (Logger.IsTrace) Logger.Trace($"EOF: Code is not Eof version {VERSION}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not Eof version {VERSION}"); return false; } sectionSizes.TypeSectionSize = GetUInt16(container, TYPESECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); if (sectionSizes.TypeSectionSize < MINIMUM_TYPESECTION_SIZE) { - if (Logger.IsTrace) Logger.Trace($"EOF: TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); return false; } @@ -266,7 +266,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => sectionSizes.CodeSectionSize = (ushort)(numberOfCodeSections * TWO_BYTE_LENGTH); if (numberOfCodeSections > MAXIMUM_NUM_CODE_SECTIONS) { - if (Logger.IsTrace) Logger.Trace($"EOF: code sections count must not exceed {MAXIMUM_NUM_CODE_SECTIONS}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, code sections count must not exceed {MAXIMUM_NUM_CODE_SECTIONS}"); return false; } @@ -279,7 +279,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => if (codeSectionSize == 0) { - if (Logger.IsTrace) Logger.Trace($"EOF: Empty Code Section are not allowed, CodeSectionSize must be > 0 but found {codeSectionSize}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Empty Code Section are not allowed, CodeSectionSize must be > 0 but found {codeSectionSize}"); return false; } @@ -296,7 +296,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * TWO_BYTE_LENGTH); if (numberOfContainerSections is > MAXIMUM_NUM_CONTAINER_SECTIONS or 0) { - if (Logger.IsTrace) Logger.Trace($"EOF: code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); return false; } @@ -309,7 +309,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => if (containerSectionSize == 0) { - if (Logger.IsTrace) Logger.Trace($"EOF: Empty Container Section are not allowed, containerSectionSize must be > 0 but found {containerSectionSize}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Empty Container Section are not allowed, containerSectionSize must be > 0 but found {containerSectionSize}"); return false; } @@ -387,44 +387,44 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida if (header.ContainerSection?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) { // move this check where `header.ExtraContainers.Count` is parsed - if (Logger.IsTrace) Logger.Trace($"EOF: initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSection?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSection?.Count}"); return false; } if (contractBody.Length != calculatedCodeLength) { - if (Logger.IsTrace) Logger.Trace("EOF: SectionSizes indicated in bundled header are incorrect, or ContainerCode is incomplete"); + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, SectionSizes indicated in bundled header are incorrect, or ContainerCode is incomplete"); return false; } if (strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size > dataBody.Length) { - if (Logger.IsTrace) Logger.Trace("EOF: DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); return false; } if (!strategy.HasFlag(ValidationStrategy.AllowTrailingBytes) && strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size != dataBody.Length) { - if (Logger.IsTrace) Logger.Trace("EOF: DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); return false; } if (codeSections.Count == 0 || codeSections.SubSectionsSizes.Any(size => size == 0)) { - if (Logger.IsTrace) Logger.Trace($"EOF: CodeSection size must follow a CodeSection, CodeSection length was {codeSections.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection size must follow a CodeSection, CodeSection length was {codeSections.Count}"); return false; } if (codeSections.Count != (typeSectionSize / MINIMUM_TYPESECTION_SIZE)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Code Sections count must match TypeSection count, CodeSection count was {codeSections.Count}, expected {typeSectionSize / MINIMUM_TYPESECTION_SIZE}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code Sections count must match TypeSection count, CodeSection count was {codeSections.Count}, expected {typeSectionSize / MINIMUM_TYPESECTION_SIZE}"); return false; } ReadOnlySpan typesection = container.Slice(typeSectionStart, typeSectionSize); if (!ValidateTypeSection(typesection)) { - if (Logger.IsTrace) Logger.Trace($"EOF: invalid typesection found"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, invalid typesection found"); return false; } @@ -463,13 +463,13 @@ bool ValidateTypeSection(ReadOnlySpan types) { if (types[INPUTS_OFFSET] != 0 || types[OUTPUTS_OFFSET] != NON_RETURNING) { - if (Logger.IsTrace) Logger.Trace($"EOF: first 2 bytes of type section must be 0s"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, first 2 bytes of type section must be 0s"); return false; } if (types.Length % MINIMUM_TYPESECTION_SIZE != 0) { - if (Logger.IsTrace) Logger.Trace($"EOF: type section length must be a product of {MINIMUM_TYPESECTION_SIZE}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, type section length must be a product of {MINIMUM_TYPESECTION_SIZE}"); return false; } @@ -481,19 +481,19 @@ bool ValidateTypeSection(ReadOnlySpan types) if (inputCount > INPUTS_MAX) { - if (Logger.IsTrace) Logger.Trace("EOF: Too many inputs"); + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Too many inputs"); return false; } if (outputCount > OUTPUTS_MAX && outputCount != NON_RETURNING) { - if (Logger.IsTrace) Logger.Trace("EOF: Too many outputs"); + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Too many outputs"); return false; } if (maxStackHeight > MAX_STACK_HEIGHT) { - if (Logger.IsTrace) Logger.Trace("EOF: Stack depth too high"); + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Stack depth too high"); return false; } } @@ -521,7 +521,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (!opcode.IsValid(IsEofContext: true)) { - if (Logger.IsTrace) Logger.Trace($"EOF: CodeSection contains undefined opcode {opcode}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains undefined opcode {opcode}"); return false; } @@ -529,7 +529,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode { if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {opcode.FastToString()} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); return false; } @@ -538,7 +538,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (rjumpdest < 0 || rjumpdest >= code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {opcode.FastToString()} Destination outside of Code bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Destination outside of Code bounds"); return false; } @@ -551,7 +551,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode { if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.JUMPF} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} Argument underflow"); return false; } @@ -559,7 +559,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (targetSectionId >= header.CodeSections.Count) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.JUMPF} to unknown code section"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} to unknown code section"); return false; } @@ -567,7 +567,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (isNonReturning && !isTargetSectionNonReturning) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.JUMPF} from non returning code-sections can only call non-returning sections"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} from non returning code-sections can only call non-returning sections"); return false; } @@ -579,7 +579,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode { if (postInstructionByte + ONE_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {opcode.FastToString()} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); return false; } BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); @@ -590,7 +590,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode { if (postInstructionByte + ONE_BYTE_LENGTH + TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RJUMPV} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Argument underflow"); return false; } @@ -598,13 +598,13 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode jumpsCount += count; if (count < MINIMUMS_ACCEPTABLE_JUMPV_JUMPTABLE_LENGTH) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RJUMPV} jumptable must have at least 1 entry"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumptable must have at least 1 entry"); return false; } if (postInstructionByte + ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RJUMPV} jumptable underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumptable underflow"); return false; } @@ -615,7 +615,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode var rjumpdest = offset + immediateValueSize + postInstructionByte; if (rjumpdest < 0 || rjumpdest >= code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RJUMPV} Destination outside of Code bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Destination outside of Code bounds"); return false; } BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpdests, ref rjumpdest); @@ -627,7 +627,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode { if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.CALLF} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Argument underflow"); return false; } @@ -635,14 +635,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (targetSectionId >= header.CodeSections.Count) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.CALLF} Invalid Section Id"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Invalid Section Id"); return false; } byte targetSectionOutputCount = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; if (targetSectionOutputCount == 0x80) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.CALLF} into non-returning function"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} into non-returning function"); return false; } @@ -652,7 +652,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (opcode is Instruction.RETF && typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80) { - if (Logger.IsTrace) Logger.Trace($"EOF: non returning sections are not allowed to use opcode {Instruction.RETF}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, non returning sections are not allowed to use opcode {Instruction.RETF}"); return false; } @@ -660,7 +660,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode { if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.DATALOADN} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.DATALOADN} Argument underflow"); return false; } @@ -668,7 +668,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (dataSectionOffset + 32 >= header.DataSection.Size) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.DATALOADN}'s immediate argument must be less than datasection.Length / 32 i.e: {header.DataSection.Size / 32}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.DATALOADN}'s immediate argument must be less than datasection.Length / 32 i.e: {header.DataSection.Size / 32}"); return false; } BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); @@ -678,14 +678,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode { if (postInstructionByte + ONE_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RETURNCONTRACT} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT} Argument underflow"); return false; } ushort containerId = code[postInstructionByte]; if ( containerId >= header.ContainerSection?.Count) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {header.ContainerSection?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {header.ContainerSection?.Count}"); return false; } BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); @@ -695,7 +695,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode { if (postInstructionByte + ONE_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.EOFCREATE} Argument underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE} Argument underflow"); return false; } @@ -704,14 +704,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (initcodeSectionId >= header.ContainerSection?.Count) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {header.CodeSections.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {header.CodeSections.Count}"); return false; } - ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value[initcodeSectionId].Start, header.ContainerSection.Value[initcodeSectionId].Size); + ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value.Start + header.ContainerSection.Value[initcodeSectionId].Start, header.ContainerSection.Value[initcodeSectionId].Size); if(IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody, out _)) { - if (Logger.IsTrace) Logger.Trace($"EOF: {Instruction.EOFCREATE}'s immediate must be a valid Eof"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must be a valid Eof"); return false; } @@ -722,7 +722,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode int len = opcode - Instruction.PUSH0; if (postInstructionByte + len > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: {opcode.FastToString()} PC Reached out of bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} PC Reached out of bounds"); return false; } BitmapHelper.HandleNumbits(len, codeBitmap, ref postInstructionByte); @@ -732,14 +732,14 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode if (pos > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: PC Reached out of bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); return false; } bool result = !BitmapHelper.CheckCollision(codeBitmap, jumpdests); if (!result) { - if (Logger.IsTrace) Logger.Trace($"EOF: Invalid Jump destination"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination"); } if (!ValidateStackState(sectionId, code, typesection)) @@ -781,7 +781,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea ushort posPostInstruction = (ushort)(programCounter + 1 + (immediates ?? 0)); if (posPostInstruction > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: PC Reached out of bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); return false; } @@ -800,13 +800,13 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if(MAX_STACK_HEIGHT - targetMaxStackHeight + inputs > currentStackBounds.Max) { - if (Logger.IsTrace) Logger.Trace($"EOF: stack head during callf must not exceed {MAX_STACK_HEIGHT}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callf must not exceed {MAX_STACK_HEIGHT}"); return false; } if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && curr_outputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual()) { - if (Logger.IsTrace) Logger.Trace($"EOF: Stack State invalid, required height {curr_outputs + inputs - outputs} but found {currentStackBounds.Max}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {curr_outputs + inputs - outputs} but found {currentStackBounds.Max}"); return false; } break; @@ -828,7 +828,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputs) { - if (Logger.IsTrace) Logger.Trace($"EOF: Stack Underflow required {inputs} but found {currentStackBounds.Min}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputs} but found {currentStackBounds.Min}"); return false; } ushort delta = (ushort)(outputs - inputs); @@ -843,7 +843,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea var expectedHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; if (expectedHeight != currentStackBounds.Min || currentStackBounds.BoundsEqual()) { - if (Logger.IsTrace) Logger.Trace($"EOF: Stack state invalid required height {expectedHeight} but found {currentStackBounds.Min}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid required height {expectedHeight} but found {currentStackBounds.Min}"); return false; } break; @@ -864,7 +864,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } else { if (recordedStackHeight[jumpDestination] != currentStackBounds) { - if (Logger.IsTrace) Logger.Trace($"EOF: Stack state invalid at {jumpDestination}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); return false; } } @@ -889,7 +889,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea { if (recordedStackHeight[jumpDestination] != currentStackBounds) { - if (Logger.IsTrace) Logger.Trace($"EOF: Stack state invalid at {jumpDestination}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); return false; } } @@ -898,7 +898,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea posPostInstruction += immediates.Value; if(posPostInstruction > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: PC Reached out of bounds"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); return false; } unreachedBytes -= immediates.Value; @@ -926,20 +926,20 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (unreachedBytes != 0) { - if (Logger.IsTrace) Logger.Trace($"EOF: bytecode has unreachable segments"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, bytecode has unreachable segments"); return false; } if (peakStackHeight != suggestedMaxHeight) { - if (Logger.IsTrace) Logger.Trace($"EOF: Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); return false; } bool result = peakStackHeight <= MAX_STACK_HEIGHT; if (!result) { - if (Logger.IsTrace) Logger.Trace($"EOF: stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); return false; } return result; From cc5961a5705f97a2b5a2a83a345d8310ae8dbc8e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 25 Jun 2024 01:01:34 +0100 Subject: [PATCH 056/255] * (temporary) added prague pyspec tests suite * alot of fixes to stack validation and other fixes --- .../PragueStateTests.cs | 28 ++++++++++++ src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 1 + .../EvmObjectFormat/EofCodeInfo.cs | 2 +- .../EvmObjectFormat/EofCodeValidator.cs | 39 +++++++++-------- .../EvmObjectFormat/EofHeader.cs | 2 +- src/Nethermind/Nethermind.Evm/EvmStack.cs | 14 +++++- src/Nethermind/Nethermind.Evm/Instruction.cs | 4 +- .../TransactionProcessor.cs | 5 ++- .../Nethermind.Evm/VirtualMachine.cs | 43 ++++++++----------- 9 files changed, 89 insertions(+), 49 deletions(-) create mode 100644 src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs new file mode 100644 index 00000000000..dd45528a7a8 --- /dev/null +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Ethereum.Test.Base; +using FluentAssertions; +using NUnit.Framework; + +namespace Ethereum.Blockchain.Pyspec.Test; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class PragueStateTests : GeneralStateTestBase +{ + [TestCaseSource(nameof(LoadTests))] + public void Test(GeneralStateTest test) => RunTest(test).Pass.Should().BeTrue(); + + private static IEnumerable LoadTests() + { + TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() + { + ArchiveName = "fixtures_eip7692.tar.gz", + ArchiveVersion = "eip7692@v1.0.4" + }, $"fixtures/state_tests/prague"); + return loader.LoadTests().Cast(); + } +} diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index 51463a51045..6f760582fbe 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -65,6 +65,7 @@ public static void HandleNumbits(int numbits, byte[] bitvec, scoped ref int pc) } else { bitvec.Set1(pc); + pc += numbits; } } /// diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index eb1ab16fc91..c7f7ea75e5e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -19,7 +19,7 @@ public class EofCodeInfo : ICodeInfo public int Version => _header.Version; public bool IsEmpty => _codeInfo.IsEmpty; public ReadOnlyMemory TypeSection { get; } - public ReadOnlyMemory CodeSection { get; } + public ReadOnlyMemory CodeSection { get; } public ReadOnlyMemory DataSection { get; } public ReadOnlyMemory ContainerSection(int index) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index f30e9ca5bae..0631b683103 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -35,7 +35,7 @@ public Worklet(ushort position, StackBounds stackHeightBounds) struct StackBounds() { public short Max = -1; - public short Min = 255; + public short Min = 1023; public void Combine(StackBounds other) { this.Max = Math.Max(this.Max, other.Max); @@ -115,7 +115,7 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) && handler.TryParseEofHeader(container, out header)) { - bool validateBody = strategy.HasFlag(ValidationStrategy.Validate); + bool validateBody = true || strategy.HasFlag(ValidationStrategy.Validate); if (validateBody && handler.ValidateBody(container, header.Value, strategy)) { if (strategy.HasFlag(ValidationStrategy.ValidateSubContainers) && header?.ContainerSection?.Count > 0) @@ -563,11 +563,13 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode return false; } + byte targetSectionOutputCount = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + byte currentSectionOutputCount = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; bool isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; - if (isNonReturning && !isTargetSectionNonReturning) + if (!isTargetSectionNonReturning && currentSectionOutputCount < targetSectionOutputCount) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} from non returning code-sections can only call non-returning sections"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} to code section with more outputs"); return false; } @@ -778,15 +780,13 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea Instruction opcode = (Instruction)code[programCounter]; (ushort? inputs, ushort? outputs, ushort? immediates) = opcode.StackRequirements(); - ushort posPostInstruction = (ushort)(programCounter + 1 + (immediates ?? 0)); + ushort posPostInstruction = (ushort)(programCounter + 1); if (posPostInstruction > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); return false; } - unreachedBytes -= (immediates ?? 0); - switch (opcode) { case Instruction.CALLF or Instruction.JUMPF: @@ -798,13 +798,13 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); targetMaxStackHeight = typesection.Slice(sectionIndex * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); - if(MAX_STACK_HEIGHT - targetMaxStackHeight + inputs > currentStackBounds.Max) + if(MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callf must not exceed {MAX_STACK_HEIGHT}"); return false; } - if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && curr_outputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual()) + if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(curr_outputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {curr_outputs + inputs - outputs} but found {currentStackBounds.Max}"); return false; @@ -831,9 +831,9 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputs} but found {currentStackBounds.Min}"); return false; } - ushort delta = (ushort)(outputs - inputs); - currentStackBounds.Max += (short)(currentStackBounds.Max + delta); - currentStackBounds.Min += (short)(currentStackBounds.Min + delta); + short delta = (short)(outputs - inputs); + currentStackBounds.Max = (short)(currentStackBounds.Max + delta); + currentStackBounds.Min = (short)(currentStackBounds.Min + delta); peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); switch (opcode) @@ -841,7 +841,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea case Instruction.RETF: { var expectedHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - if (expectedHeight != currentStackBounds.Min || currentStackBounds.BoundsEqual()) + 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; @@ -851,7 +851,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea case Instruction.RJUMP or Instruction.RJUMPI: { short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); - int jumpDestination = posPostInstruction + offset; + int jumpDestination = posPostInstruction + immediates.Value + offset; if(opcode is Instruction.RJUMPI) { @@ -869,7 +869,6 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } } - unreachedBytes -= immediates.Value; break; } case Instruction.RJUMPV: @@ -901,22 +900,24 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); return false; } - unreachedBytes -= immediates.Value; break; } default: { - unreachedBytes -= immediates.Value; posPostInstruction += immediates.Value; break; } } + unreachedBytes -= 1 + immediates.Value; programCounter = posPostInstruction; if (opcode.IsTerminating()) { - currentStackBounds = recordedStackHeight[programCounter]; + if(programCounter < code.Length) + { + currentStackBounds = recordedStackHeight[programCounter]; + } } else { currentStackBounds.Combine(recordedStackHeight[programCounter]); @@ -936,7 +937,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea return false; } - bool result = peakStackHeight <= MAX_STACK_HEIGHT; + bool result = peakStackHeight < MAX_STACK_HEIGHT; if (!result) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index c3e13c2f60e..924d3c91072 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -39,7 +39,7 @@ private int[] SubSectionsSizesAcc subSectionsSizesAcc[0] = 0; for (var i = 1; i < SubSectionsSizes.Length; i++) { - subSectionsSizesAcc[i] = subSectionsSizesAcc[i - 1] + SubSectionsSizes[i]; + subSectionsSizesAcc[i] = subSectionsSizesAcc[i - 1] + SubSectionsSizes[i-1]; } } diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 8066f7b3811..3fd27b5fb05 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -340,6 +340,18 @@ public Span PopWord256() return _bytes.Slice(Head * WordSize, WordSize); } + public bool PopWord256(out Span word) + { + if (Head-- == 0) + { + word = default; + return false; + } + + word = _bytes.Slice(Head * WordSize, WordSize); + return true; + } + public byte PopByte() { if (Head-- == 0) @@ -432,7 +444,7 @@ public readonly bool Exchange(int n, int m) ref byte bytes = ref MemoryMarshal.GetReference(_bytes); ref byte bottom = ref Unsafe.Add(ref bytes, (Head - n - m - 1) * WordSize); - ref byte top = ref Unsafe.Add(ref bytes, (Head - n - 1) * WordSize); + ref byte top = ref Unsafe.Add(ref bytes, (Head - n - 2) * WordSize); Word buffer = Unsafe.ReadUnaligned(ref bottom); Unsafe.WriteUnaligned(ref bottom, Unsafe.ReadUnaligned(ref top)); diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 7514161be0b..6abfd6e0278 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -201,6 +201,7 @@ public enum Instruction : byte } public static class InstructionExtensions { + public static int GetImmediateCount(this Instruction instruction, bool IsEofContext, byte jumpvCount = 0) => instruction switch @@ -213,6 +214,7 @@ public static int GetImmediateCount(this Instruction instruction, bool IsEofCont { Instruction.RETF or Instruction.INVALID or Instruction.STOP or Instruction.RETURN or Instruction.REVERT => true, Instruction.JUMPF or Instruction.RETURNCONTRACT => true, + Instruction.RJUMP => true, // Instruction.SELFDESTRUCT => true _ => false }; @@ -344,7 +346,7 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.DATALOAD => (1, 1, 0), Instruction.DATALOADN => (0, 1, 2), Instruction.DATASIZE => (0, 1, 0), - Instruction.DATACOPY => (3, 1, 0), + Instruction.DATACOPY => (3, 0, 0), Instruction.RJUMPV => (1, 0, null), // null indicates this is a dynamic multi-bytes opcode Instruction.SWAPN => (null, null, 1), diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 5deb3465a20..33b382d954a 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -428,8 +428,9 @@ protected TransactionResult BuildExecutionEnvironment( } } else { - codeInfo = _codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec); - (codeInfo as CodeInfo).AnalyseInBackgroundIfRequired(); + codeInfo = _codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec); + if(codeInfo is CodeInfo eofv0CodeInfo) + eofv0CodeInfo.AnalyseInBackgroundIfRequired(); } env = new ExecutionEnvironment diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index a7bd3a5dde0..651f24aec67 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -417,9 +417,9 @@ public TransactionSubstate Run(EvmState state, IWorldState worl bytecodeResultArray = bytecodeResult.ToArray(); } - bool invalidCode = !EvmObjectFormat.IsValidEof(bytecodeResultArray, EvmObjectFormat.ValidationStrategy.ValidateFullBody, out _) + bool invalidCode = !isEof_invalidated && !EvmObjectFormat.IsValidEof(bytecodeResultArray, EvmObjectFormat.ValidationStrategy.ValidateFullBody, out _) && bytecodeResultArray.Length < spec.MaxCodeSize; - long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray.Length, spec); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray?.Length ?? 0, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { ReadOnlyMemory code = callResult.Output.Bytes; @@ -976,8 +976,7 @@ private CallResult ExecuteCode _returnDataBuffer.Length) + + if (env.CodeInfo.Version == 0 && UInt256.AddOverflow(result, b, out c) || c > _returnDataBuffer.Length) { goto AccessViolation; } @@ -1486,11 +1486,6 @@ private CallResult ExecuteCode _returnDataBuffer.Length) - { - goto AccessViolation; - } - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, 32)) goto OutOfGas; slice = _returnDataBuffer.Span.SliceWithZeroPadding(a, 32); @@ -1879,8 +1874,8 @@ private CallResult ExecuteCode> 0x04; - int m = (int)codeSection[programCounter] & 0x0f; + int n = 1 + (int)(codeSection[programCounter] >> 0x04); + int m = 1 + (int)(codeSection[programCounter] & 0x0f); stack.Exchange(n, m); @@ -2191,17 +2186,16 @@ private CallResult ExecuteCode 0) { if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; - var caseV = stack.PopByte(); - var maxIndex = codeSection[programCounter++]; - var immediateValueSize = (maxIndex + 1) * EvmObjectFormat.TWO_BYTE_LENGTH; - if (caseV <= maxIndex) + stack.PopUInt256(out a); + var count = codeSection[programCounter] + 1; + var immediates = (ushort)(count * EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.ONE_BYTE_LENGTH); + if (a < count) { - int caseOffset = codeSection.Slice( - programCounter + caseV * EvmObjectFormat.TWO_BYTE_LENGTH, - EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); - programCounter += caseOffset; + int case_v = programCounter + EvmObjectFormat.ONE_BYTE_LENGTH + (int)a * EvmObjectFormat.TWO_BYTE_LENGTH; + int offset = codeSection.Slice(case_v, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); + programCounter += offset; } - programCounter += immediateValueSize; + programCounter += immediates; break; } goto InvalidInstruction; @@ -2670,7 +2664,6 @@ private EvmExceptionType InstructionEofCall(EvmState vmState, ref return (EvmExceptionType.OutOfGas, null); if (!stack.PopUInt256(out UInt256 value) || - !stack.PopUInt256(out UInt256 salt) || + !stack.PopWord256(out Span salt) || !stack.PopUInt256(out UInt256 dataOffset) || !stack.PopUInt256(out UInt256 dataSize)) return (EvmExceptionType.StackUnderflow, null); @@ -2956,7 +2951,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; if (!UpdateGas(callGas, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt.ToBytes(), initCode.Span, env.InputData.Span); + Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initCode.Span, env.InputData.Span); if (spec.UseHotAndColdStorage) { From 765aed300b3114391385dcae1e3b9c5df6ec6f4e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 25 Jun 2024 09:37:46 +0100 Subject: [PATCH 057/255] fix some failing tests : - jump stack validation - Data opcodes (datacopy mistake in gas calculation --- .../EvmObjectFormat/EofCodeValidator.cs | 43 +++++++++---------- src/Nethermind/Nethermind.Evm/Instruction.cs | 2 +- .../Nethermind.Evm/VirtualMachine.cs | 6 +-- src/tests | 2 +- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 0631b683103..b1062dbcf7e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -668,7 +668,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode ushort dataSectionOffset = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); - if (dataSectionOffset + 32 >= header.DataSection.Size) + if (dataSectionOffset + 32 > 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: {header.DataSection.Size / 32}"); return false; @@ -763,7 +763,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); - ushort curr_outputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + ushort currrentSectionOutputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; short peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; int unreachedBytes = code.Length; @@ -790,13 +790,13 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea switch (opcode) { case Instruction.CALLF or Instruction.JUMPF: - ushort sectionIndex = code.Slice(programCounter + 1, immediates.Value).ReadEthUInt16(); - inputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + ushort targetSectionId = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); + inputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - outputs = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - isTargetSectionNonReturning = typesection[sectionIndex * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; + outputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); - targetMaxStackHeight = typesection.Slice(sectionIndex * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); + targetMaxStackHeight = typesection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); if(MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) { @@ -804,14 +804,14 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea return false; } - if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(curr_outputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) + if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currrentSectionOutputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {curr_outputs + inputs - outputs} but found {currentStackBounds.Max}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currrentSectionOutputs + inputs - outputs} but found {currentStackBounds.Max}"); return false; } break; case Instruction.DUPN: - byte imm = code[programCounter + 1]; + byte imm = code[posPostInstruction]; inputs = (ushort)(imm + 1); outputs = (ushort)(inputs + 1); break; @@ -820,8 +820,8 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea outputs = inputs = (ushort)(2 + imm); break; case Instruction.EXCHANGE: - byte imm_n = (byte)(code[programCounter + 1] >> 4); - byte imm_m = (byte)(code[programCounter + 1] & 0x0F); + byte imm_n = (byte)(code[posPostInstruction] >> 4); + byte imm_m = (byte)(code[posPostInstruction] & 0x0F); outputs = inputs = (ushort)(imm_n + imm_m + 3); break; } @@ -831,9 +831,13 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputs} but found {currentStackBounds.Min}"); return false; } - short delta = (short)(outputs - inputs); - currentStackBounds.Max = (short)(currentStackBounds.Max + delta); - currentStackBounds.Min = (short)(currentStackBounds.Min + delta); + + if(!opcode.IsTerminating()) + { + short delta = (short)(outputs - inputs); + currentStackBounds.Max = (short)(currentStackBounds.Max + delta); + currentStackBounds.Min = (short)(currentStackBounds.Min + delta); + } peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); switch (opcode) @@ -855,7 +859,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if(opcode is Instruction.RJUMPI) { - recordedStackHeight[posPostInstruction].Combine(currentStackBounds); + recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); } if(jumpDestination > programCounter) @@ -902,15 +906,10 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } break; } - default: - { - posPostInstruction += immediates.Value; - break; - } } unreachedBytes -= 1 + immediates.Value; - programCounter = posPostInstruction; + programCounter += 1 + immediates.Value; if (opcode.IsTerminating()) { diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 6abfd6e0278..bded8801d1e 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -353,7 +353,7 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.DUPN => (null, null, 1), Instruction.EXCHANGE => (null, null, 1), - Instruction.EXTCALL => (3, 1, 0), + Instruction.EXTCALL => (4, 1, 0), Instruction.EXTSTATICCALL => (3, 1, 0), Instruction.EXTDELEGATECALL => (3, 1, 0), _ => throw new NotImplementedException($"opcode {instruction} not implemented yet"), diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 651f24aec67..f2cc9012c1e 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2345,17 +2345,13 @@ private CallResult ExecuteCode UInt256.Zero) { - gasAvailable -= GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size); - if (!UpdateGas(gasAvailable, ref gasAvailable) || + if (!UpdateGas(GasCostOf.Memory + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable) || !UpdateMemoryCost(vmState, ref gasAvailable, in memOffset, size)) goto OutOfGas; diff --git a/src/tests b/src/tests index 661356317ac..ebbaa54c7a9 160000 --- a/src/tests +++ b/src/tests @@ -1 +1 @@ -Subproject commit 661356317ac6df52208d54187e692472a25a01f8 +Subproject commit ebbaa54c7a90e74313b846369fe87e9bd3a58369 From 0112f8bcff38907daf6e2983fc9affea8df93716 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 26 Jun 2024 13:44:27 +0100 Subject: [PATCH 058/255] reimplement *CALL opcodes --- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 2 +- src/Nethermind/Nethermind.Evm/EvmException.cs | 1 + .../EvmObjectFormat/EofCodeValidator.cs | 7 +- src/Nethermind/Nethermind.Evm/EvmStack.cs | 12 ++ .../Nethermind.Evm/VirtualMachine.cs | 117 +++++++----------- 5 files changed, 64 insertions(+), 75 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index 6f760582fbe..4f288fa6ca3 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -114,7 +114,7 @@ private static void Set16(this byte[] bitvec, int pos) private const uint Vector256ByteCount = 32; private const uint Vector256IntCount = 8; - public static bool CheckCollision(byte[] codeSegments, byte[] jumpmask) + public static bool CheckCollision(ReadOnlySpan codeSegments, ReadOnlySpan jumpmask) { int count = Math.Min(codeSegments.Length, jumpmask.Length); diff --git a/src/Nethermind/Nethermind.Evm/EvmException.cs b/src/Nethermind/Nethermind.Evm/EvmException.cs index 7bc74c52c79..a1c76b8390f 100644 --- a/src/Nethermind/Nethermind.Evm/EvmException.cs +++ b/src/Nethermind/Nethermind.Evm/EvmException.cs @@ -22,6 +22,7 @@ public enum EvmExceptionType InvalidSubroutineReturn, InvalidJumpDestination, AccessViolation, + AddressOutOfRange, StaticCallViolation, PrecompileFailure, TransactionCollision, diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index b1062dbcf7e..4555fbf745c 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -184,7 +184,6 @@ internal enum Separator : byte internal const byte MINIMUM_HEADER_SIZE = VERSION_OFFSET + MINIMUM_HEADER_SECTION_SIZE + MINIMUM_HEADER_SECTION_SIZE + TWO_BYTE_LENGTH - + MINIMUM_HEADER_SECTION_SIZE + TWO_BYTE_LENGTH + MINIMUM_HEADER_SECTION_SIZE + ONE_BYTE_LENGTH; @@ -210,9 +209,7 @@ internal enum Separator : byte 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 - + MINIMUM_CONTAINERSECTION_SIZE; // minimum container section body size - + + MINIMUM_DATASECTION_SIZE; // minimum data section body size public bool TryParseEofHeader(ReadOnlySpan container, out EofHeader? header) { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -711,7 +708,7 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode } ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value.Start + header.ContainerSection.Value[initcodeSectionId].Start, header.ContainerSection.Value[initcodeSectionId].Size); - if(IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody, out _)) + if(!IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody | ValidationStrategy.ValidateInitcodeMode, out _)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must be a valid Eof"); return false; diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 3fd27b5fb05..17ff90332fc 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -320,6 +320,18 @@ public Address PopAddress() return new Address(_bytes.Slice(Head * WordSize + WordSize - AddressSize, AddressSize).ToArray()); } + public bool PopAddress(out Address address) + { + if (Head-- == 0) + { + address = null; + return false; + } + + address = new Address(_bytes.Slice(Head * WordSize + WordSize - AddressSize, AddressSize).ToArray()); + return true; + } + public ref byte PopBytesByRef() { if (Head-- == 0) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f2cc9012c1e..0ebfb18b893 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -43,6 +43,7 @@ namespace Nethermind.Evm; using Nethermind.Core.Collections; using System.Diagnostics; +using System.Runtime.Intrinsics.X86; public class VirtualMachine : IVirtualMachine { @@ -100,6 +101,7 @@ internal readonly ref struct CallResult public static CallResult StackOverflowException => new(EvmExceptionType.StackOverflow); // TODO: use these to avoid CALL POP attacks public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); + public static CallResult InvalidAddressRange => new(EvmExceptionType.AddressOutOfRange); public static object BoxedEmpty { get; } = new object(); public static CallResult Empty(int fromVersion) => new(default, null, fromVersion); @@ -1486,7 +1488,6 @@ private CallResult ExecuteCode targetBytes); + stack.PopUInt256(out UInt256 dataOffset); + stack.PopUInt256(out UInt256 dataLength); + UInt256 callValue; switch (instruction) { @@ -2671,80 +2672,40 @@ private EvmExceptionType InstructionEofCall(); - stack.PushZero(); - return EvmExceptionType.None; - } - - long gasExtra = 0L; + Address caller = instruction == Instruction.EXTDELEGATECALL ? env.Caller : env.ExecutingAccount; + Address targetAddress = new Address(targetBytes.ToArray()); - if (!transferValue.IsZero) - { - gasExtra += GasCostOf.CallValue; - } + if (!UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataLength)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, targetAddress, spec)) return EvmExceptionType.OutOfGas; - if (!spec.ClearEmptyAccountWhenTouched && !_state.AccountExists(target)) + if ((!spec.ClearEmptyAccountWhenTouched && !_state.AccountExists(targetAddress)) + || (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && _state.IsDeadAccount(targetAddress))) { - gasExtra += GasCostOf.NewAccount; - } - else if (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && _state.IsDeadAccount(target)) - { - gasExtra += GasCostOf.NewAccount; + UpdateGas(GasCostOf.NewAccount, ref gasAvailable); } - if (!UpdateGas(spec.GetCallCost(), ref gasAvailable) || - !UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataLength) || - !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; - - UInt256 gasLimit = (UInt256)(gasAvailable - gasAvailable / 64); - - if (gasLimit >= long.MaxValue) return EvmExceptionType.OutOfGas; + long callGas = gasAvailable - Math.Max(gasAvailable/64, 5000); + if (callGas >= long.MaxValue) return EvmExceptionType.OutOfGas; + if(!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; - long gasLimitUl = (long)gasLimit; - if (!UpdateGas(gasLimitUl, ref gasAvailable)) return EvmExceptionType.OutOfGas; - - if (!transferValue.IsZero) - { - if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportExtraGasPressure(GasCostOf.CallStipend); - gasLimitUl += GasCostOf.CallStipend; - } - - if (env.CallDepth >= MaxCallDepth || - !transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) + if(callGas < GasCostOf.CallStipend || env.CallDepth >= MaxCallDepth || !transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) { _returnDataBuffer = Array.Empty(); - stack.PushZero(); + stack.PushOne(); if (typeof(TTracingInstructions) == typeof(IsTracing)) { @@ -2757,8 +2718,26 @@ private EvmExceptionType InstructionEofCall(); + stack.PushZero(); return EvmExceptionType.None; } @@ -2772,18 +2751,18 @@ private EvmExceptionType InstructionEofCall Date: Sun, 30 Jun 2024 19:39:16 +0100 Subject: [PATCH 059/255] Minor code changes --- .../Nethermind.Evm/AddressExtensions.cs | 21 +---- .../EvmObjectFormat/EofCodeInfo.cs | 20 ++-- .../EvmObjectFormat/EofCodeValidator.cs | 40 +++++--- .../Nethermind.Evm/ExecutionType.cs | 9 +- .../Nethermind.Evm/VirtualMachine.cs | 91 ++++++++----------- 5 files changed, 90 insertions(+), 91 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/AddressExtensions.cs b/src/Nethermind/Nethermind.Evm/AddressExtensions.cs index a2527eeeef0..f6a13ba9ccd 100644 --- a/src/Nethermind/Nethermind.Evm/AddressExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/AddressExtensions.cs @@ -25,32 +25,19 @@ public static Address From(Address? deployingAddress, in UInt256 nonce) return new Address(in contractAddressKeccak); } - public static Address From(Address deployingAddress, ReadOnlySpan salt, ReadOnlySpan initCode, ReadOnlySpan auxData) + public static Address From(Address deployingAddress, ReadOnlySpan salt, ReadOnlySpan initCode) { // sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code) ++ sha3(aux_data)) - Span bytes = new byte[1 + Address.Size + 32 + salt.Length + auxData.Length]; + Span bytes = new byte[1 + Address.Size + Keccak.Size + salt.Length]; bytes[0] = 0xff; deployingAddress.Bytes.CopyTo(bytes.Slice(1, 20)); - salt.CopyTo(bytes.Slice(21, salt.Length)); - ValueKeccak.Compute(initCode).BytesAsSpan.CopyTo(bytes.Slice(21 + salt.Length, 32)); - auxData.CopyTo(bytes.Slice(21 + salt.Length + 32, auxData.Length)); + salt.CopyTo(bytes.Slice(1 + Address.Size, salt.Length)); + ValueKeccak.Compute(initCode).BytesAsSpan.CopyTo(bytes.Slice(1 + Address.Size + salt.Length, Keccak.Size)); ValueHash256 contractAddressKeccak = ValueKeccak.Compute(bytes); Span addressBytes = contractAddressKeccak.BytesAsSpan[12..]; return new Address(addressBytes.ToArray()); } - public static Address From(Address deployingAddress, ReadOnlySpan salt, ReadOnlySpan initCode) - { - // sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))) - Span bytes = new byte[1 + Address.Size + 32 + salt.Length]; - bytes[0] = 0xff; - deployingAddress.Bytes.CopyTo(bytes.Slice(1, 20)); - salt.CopyTo(bytes.Slice(21, salt.Length)); - ValueKeccak.Compute(initCode).BytesAsSpan.CopyTo(bytes.Slice(21 + salt.Length, 32)); - - ValueHash256 contractAddressKeccak = ValueKeccak.Compute(bytes); - return new Address(in contractAddressKeccak); - } // See https://eips.ethereum.org/EIPS/eip-7610 public static bool IsNonZeroAccount(this Address contractAddress, IReleaseSpec spec, ICodeInfoRepository codeInfoRepository, IWorldState state) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index c7f7ea75e5e..f1861bd294e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -13,10 +13,10 @@ public class EofCodeInfo : ICodeInfo { private readonly ICodeInfo _codeInfo; - private readonly EofHeader _header; + public EofHeader Header { get; private set; } public ReadOnlyMemory MachineCode => _codeInfo.MachineCode; public IPrecompile? Precompile => _codeInfo.Precompile; - public int Version => _header.Version; + public int Version => Header.Version; public bool IsEmpty => _codeInfo.IsEmpty; public ReadOnlyMemory TypeSection { get; } public ReadOnlyMemory CodeSection { get; } @@ -29,14 +29,14 @@ public ReadOnlyMemory ContainerSection(int index) return Memory.Empty; else { - return MachineCode.Slice(_header.ContainerSection.Value.Start + offset.Value.Start, offset.Value.Size); + return MachineCode.Slice(Header.ContainerSection.Value.Start + offset.Value.Start, offset.Value.Size); } } - public SectionHeader CodeSectionOffset(int sectionId) => _header.CodeSections[sectionId]; + public SectionHeader CodeSectionOffset(int sectionId) => Header.CodeSections[sectionId]; public SectionHeader? ContainerSectionOffset(int containerId) => - _header.ContainerSection is null + Header.ContainerSection is null ? null - : _header.ContainerSection.Value[containerId]; + : Header.ContainerSection.Value[containerId]; public (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) { ReadOnlySpan typesectionSpan = TypeSection.Span; @@ -52,9 +52,9 @@ _header.ContainerSection is null public EofCodeInfo(ICodeInfo codeInfo, in EofHeader header) { _codeInfo = codeInfo; - _header = header; - TypeSection = MachineCode.Slice(_header.TypeSection.Start, _header.TypeSection.Size); - DataSection = MachineCode.Slice(_header.DataSection.Start, _header.DataSection.Size); - CodeSection = MachineCode.Slice(_header.CodeSections.Start, _header.CodeSections.Size); + Header = header; + TypeSection = MachineCode.Slice(Header.TypeSection.Start, Header.TypeSection.Size); + DataSection = MachineCode.Slice(Header.DataSection.Start, Header.DataSection.Size); + CodeSection = MachineCode.Slice(Header.CodeSections.Start, Header.CodeSections.Size); } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 4555fbf745c..1bc51922bfa 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -57,8 +57,10 @@ public enum ValidationStrategy ValidateSubContainers = Validate | 2, ValidateFullBody = Validate | 4, ValidateInitcodeMode = Validate | 8, - AllowTrailingBytes = Validate | 16, - ExractHeader = 32, + ValidateRuntimeMode = Validate | 16, + AllowTrailingBytes = Validate | 32, + ExractHeader = 64, + HasEofMagic = 128, } @@ -111,6 +113,12 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s return true; } + if(strategy.HasFlag(ValidationStrategy.HasEofMagic) && !container.StartsWith(MAGIC)) + { + header = null; + return false; + } + if (container.Length > VERSION_OFFSET && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) && handler.TryParseEofHeader(container, out header)) @@ -440,10 +448,8 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida visitedSections[sectionIdx] = true; (int codeSectionStartOffset, int codeSectionSize) = header.CodeSections[sectionIdx]; - bool isInitCodeValidationMode = strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode); - bool isNonReturning = typesection[sectionIdx * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSectionStartOffset, codeSectionSize); - if (!ValidateInstructions(sectionIdx, isNonReturning, isInitCodeValidationMode, typesection, code, header, container, validationQueue, out ushort jumpsCount)) + if (!ValidateInstructions(sectionIdx, strategy, typesection, code, header, container, validationQueue)) { ArrayPool.Shared.Return(visitedSections, true); return false; @@ -497,11 +503,10 @@ bool ValidateTypeSection(ReadOnlySpan types) return true; } - bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcodeMode, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist, out ushort jumpsCount) + bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist) { byte[] codeBitmap = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); byte[] jumpdests = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); - jumpsCount = 1; try { int pos; @@ -511,7 +516,12 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode Instruction opcode = (Instruction)code[pos]; int postInstructionByte = pos + 1; - if(isInitcodeMode && opcode is Instruction.RETURN or Instruction.STOP) + if (strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) && opcode is Instruction.RETURN or Instruction.STOP) + { + return false; + } + + if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) && opcode is Instruction.RETURNCONTRACT) { return false; } @@ -539,7 +549,6 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode return false; } - jumpsCount += opcode is Instruction.RJUMP ? ONE_BYTE_LENGTH : TWO_BYTE_LENGTH; BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpdests, ref rjumpdest); BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); } @@ -594,7 +603,6 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode } ushort count = (ushort)(code[postInstructionByte] + 1); - jumpsCount += count; if (count < MINIMUMS_ACCEPTABLE_JUMPV_JUMPTABLE_LENGTH) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumptable must have at least 1 entry"); @@ -681,12 +689,20 @@ bool ValidateInstructions(ushort sectionId, bool isNonReturning, bool isInitcode return false; } - ushort containerId = code[postInstructionByte]; - if ( containerId >= header.ContainerSection?.Count) + ushort runtimeContainerId = code[postInstructionByte]; + if (runtimeContainerId >= header.ContainerSection?.Count) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {header.ContainerSection?.Count}"); return false; } + + ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value.Start + header.ContainerSection.Value[runtimeContainerId].Start, header.ContainerSection.Value[runtimeContainerId].Size); + if (!IsValidEof(subcontainer, ValidationStrategy.ValidateRuntimeMode, out _)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate must be a valid Eof"); + return false; + } + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); } diff --git a/src/Nethermind/Nethermind.Evm/ExecutionType.cs b/src/Nethermind/Nethermind.Evm/ExecutionType.cs index 47b1902e982..e8a5f138769 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionType.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionType.cs @@ -29,6 +29,10 @@ 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.") }; } @@ -39,12 +43,15 @@ public enum ExecutionType TRANSACTION, CALL, STATICCALL, - CALLCODE, DELEGATECALL, + CALLCODE, CREATE, CREATE2, EOFCREATE, TXCREATE, + EOFCALL, + EOFSTATICCALL, + EOFDELEGATECALL, } // ReSharper restore IdentifierTypo InconsistentNaming } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3c91fe0c116..7fdb395d32b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -404,7 +404,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl int containerIndex = callResult.Output.ContainerIndex.Value; ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; ReadOnlySpan container = previousState.Env.CodeInfo.ContainerSection(containerIndex).Span; - bool isEof_invalidated = !EvmObjectFormat.TryExtractHeader(container, out EofHeader? header) && header?.DataSection.Size != auxExtraData.Length; + bool isEof_invalidated = !EvmObjectFormat.TryExtractHeader(container, out EofHeader? header) || header?.DataSection.Size != auxExtraData.Length; byte[] bytecodeResultArray = null; if (!isEof_invalidated) @@ -2291,7 +2291,7 @@ private CallResult ExecuteCode(EvmState vmState, ref where TTracing : struct, IIsTracing { ref readonly ExecutionEnvironment env = ref vmState.Env; - + EofCodeInfo container = env.CodeInfo as EofCodeInfo; var currentContext = ExecutionType.EOFCREATE; if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); @@ -2877,17 +2877,12 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref if (!UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataSize)) return (EvmExceptionType.OutOfGas, null); - ReadOnlyMemory initCode = ReadOnlyMemory.Empty; int initCodeIdx = codeSection[vmState.ProgramCounter++]; - initCode = env.CodeInfo.ContainerSection(initCodeIdx); - - //EIP-3860 - if (spec.IsEip3860Enabled) - { - if (initCode.Length > spec.MaxInitCodeSize) return (EvmExceptionType.InvalidCode, null); - } + ReadOnlyMemory initCode = container.ContainerSection(initCodeIdx); + int initcode_size = container.Header.ContainerSection.Value[initCodeIdx].Size; + - long gasCost = GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initCode.Length); + long gasCost = GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcode_size); if (!UpdateGas(gasCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); @@ -2900,8 +2895,6 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } - Span calldata = vmState.Memory.LoadSpan(dataOffset, dataSize); - UInt256 balance = _state.GetBalance(env.ExecutingAccount); if (value > balance) { @@ -2910,6 +2903,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } + Span calldata = vmState.Memory.LoadSpan(dataOffset, dataSize); + UInt256 accountNonce = _state.GetNonce(env.ExecutingAccount); UInt256 maxNonce = ulong.MaxValue; if (accountNonce >= maxNonce) @@ -2925,7 +2920,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; if (!UpdateGas(callGas, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initCode.Span, env.InputData.Span); + Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initCode.Span); if (spec.UseHotAndColdStorage) { @@ -3079,7 +3074,21 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref vmState.WarmUp(contractAddress); } - _state.IncrementNonce(env.ExecutingAccount); + // Do not add the initCode to the cache as it is + // pointing to data in this tx and will become invalid + // for another tx as returned to pool. + if (CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.HasEofMagic)) + { + _returnDataBuffer = Array.Empty(); + stack.PushZero(); + UpdateGasUp(callGas, ref gasAvailable); + return (EvmExceptionType.None, null); + } + + if (codeinfo is CodeInfo classicalCode) + { + classicalCode.AnalyseInBackgroundIfRequired(); + } Snapshot snapshot = _state.TakeSnapshot(); @@ -3101,14 +3110,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - // Do not add the initCode to the cache as it is - // pointing to data in this tx and will become invalid - // for another tx as returned to pool. - CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.ExractHeader); - if(codeinfo is CodeInfo classicalCode) - { - classicalCode.AnalyseInBackgroundIfRequired(); - } + _state.IncrementNonce(env.ExecutingAccount); ExecutionEnvironment callEnv = new ( @@ -3364,6 +3366,7 @@ private CallResult GetFailureReturn(long gasAvailable, Evm EvmExceptionType.StackUnderflow => CallResult.StackUnderflowException, EvmExceptionType.InvalidJumpDestination => CallResult.InvalidJumpDestination, EvmExceptionType.AccessViolation => CallResult.AccessViolationException, + EvmExceptionType.AddressOutOfRange => CallResult.InvalidAddressRange, _ => throw new ArgumentOutOfRangeException(nameof(exceptionType), exceptionType, "") }; } @@ -3443,29 +3446,15 @@ private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExc } private static ExecutionType GetCallExecutionType(Instruction instruction, bool isPostMerge = false) - { - ExecutionType executionType; - if (instruction is Instruction.CALL or Instruction.EXTCALL) - { - executionType = ExecutionType.CALL; - } - else if (instruction is Instruction.DELEGATECALL or Instruction.EXTDELEGATECALL) - { - executionType = ExecutionType.DELEGATECALL; - } - else if (instruction is Instruction.STATICCALL or Instruction.EXTSTATICCALL) - { - executionType = ExecutionType.STATICCALL; - } - else if (instruction is Instruction.CALLCODE) - { - executionType = ExecutionType.CALLCODE; - } - else - { - throw new NotSupportedException($"Execution type is undefined for {instruction.GetName(isPostMerge)}"); - } - - return executionType; - } + => instruction switch + { + Instruction.CALL => ExecutionType.CALL, + Instruction.DELEGATECALL => ExecutionType.DELEGATECALL, + Instruction.STATICCALL => ExecutionType.STATICCALL, + Instruction.CALLCODE => ExecutionType.CALLCODE, + Instruction.EXTCALL => ExecutionType.EOFCALL, + Instruction.EXTDELEGATECALL => ExecutionType.EOFDELEGATECALL, + Instruction.EXTSTATICCALL => ExecutionType.EOFSTATICCALL, + _ => throw new NotSupportedException($"Execution type is undefined for {instruction.GetName(isPostMerge)}") + }; } From 6b28af51ca2e90c8bdb1b744e016af0176ae115a Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 1 Jul 2024 15:55:39 +0100 Subject: [PATCH 060/255] added status error codes --- src/Nethermind/Nethermind.Core/Extensions/Bytes.cs | 1 + src/Nethermind/Nethermind.Evm/ExecutionType.cs | 12 ++++++++++++ src/Nethermind/Nethermind.Evm/StatusCode.cs | 9 +++++++++ src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 8 +++++--- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index fc06582ae42..995bb1d501e 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -29,6 +29,7 @@ public static unsafe partial class Bytes public static readonly BytesComparer Comparer = new(); public static readonly ReadOnlyMemory ZeroByte = new byte[] { 0 }; public static readonly ReadOnlyMemory OneByte = new byte[] { 1 }; + public static readonly ReadOnlyMemory TwoByte = new byte[] { 2 }; private class BytesEqualityComparer : EqualityComparer { diff --git a/src/Nethermind/Nethermind.Evm/ExecutionType.cs b/src/Nethermind/Nethermind.Evm/ExecutionType.cs index e8a5f138769..2f6498b06a9 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionType.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionType.cs @@ -19,6 +19,18 @@ public static bool IsAnyCreateEof(this ExecutionType executionType) => public static bool IsAnyCreate(this ExecutionType executionType) => IsAnyCreateLegacy(executionType) || IsAnyCreateEof(executionType); + [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 { diff --git a/src/Nethermind/Nethermind.Evm/StatusCode.cs b/src/Nethermind/Nethermind.Evm/StatusCode.cs index 67d23340f69..fd17b3b7e96 100644 --- a/src/Nethermind/Nethermind.Evm/StatusCode.cs +++ b/src/Nethermind/Nethermind.Evm/StatusCode.cs @@ -13,4 +13,13 @@ 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/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 7fdb395d32b..22b51495158 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -458,7 +458,9 @@ public TransactionSubstate Run(EvmState state, IWorldState worl else { _returnDataBuffer = callResult.Output.Bytes; - previousCallResult = callResult.PrecompileSuccess.HasValue ? (callResult.PrecompileSuccess.Value ? StatusCode.SuccessBytes : StatusCode.FailureBytes) : StatusCode.SuccessBytes; + 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)); previousCallOutputDestination = (ulong)previousState.OutputDestination; if (previousState.IsPrecompile) @@ -485,7 +487,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl { worldState.Restore(previousState.Snapshot); _returnDataBuffer = callResult.Output.Bytes; - previousCallResult = StatusCode.FailureBytes; + previousCallResult = previousState.ExecutionType.IsAnyCallEof() ? EofStatusCode.RevertBytes : StatusCode.FailureBytes; previousCallOutput = callResult.Output.Bytes.Span.SliceWithZeroPadding(0, Math.Min(callResult.Output.Bytes.Length, (int)previousState.OutputLength)); previousCallOutputDestination = (ulong)previousState.OutputDestination; @@ -521,7 +523,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl return new TransactionSubstate(ex is OverflowException ? EvmExceptionType.Other : (ex as EvmException).ExceptionType, isTracing); } - previousCallResult = StatusCode.FailureBytes; + previousCallResult = currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; previousCallOutputDestination = UInt256.Zero; _returnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; From 71f7de17841a4b627c41d70c4725763e7f3a0222 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 2 Jul 2024 10:49:17 +0100 Subject: [PATCH 061/255] fix some failing tests --- .../Nethermind.Consensus/Validators/TxValidator.cs | 1 + src/Nethermind/Nethermind.Core/Transaction.cs | 1 - .../Nethermind.Evm/IntrinsicGasCalculator.cs | 13 ------------- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 10 ++-------- 4 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index 258ec3d18d0..4554062a513 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -149,6 +149,7 @@ private bool ValidateSignature(Transaction tx, IReleaseSpec spec) return !spec.ValidateChainId; } + private static bool Validate4844Fields(Transaction transaction, ref string error) { // Execution-payload version verification diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index bacb5c5561f..06fcbb7f6ce 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -20,7 +20,6 @@ namespace Nethermind.Core public class Transaction { public const int BaseTxGasCost = 21000; - public const int MaxInitcodeCount = 256; public ulong? ChainId { get; set; } /// diff --git a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs index b82f068b561..f9ecf0c1a43 100644 --- a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs +++ b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs @@ -39,19 +39,6 @@ private static long CreateCost(Transaction transaction, IReleaseSpec releaseSpec } private static long DataCost(Transaction transaction, IReleaseSpec releaseSpec) - { - Span data = transaction.Data.GetValueOrDefault().Span; - long dataCost = CalculateCalldataCost(transaction, releaseSpec); - - if (transaction.IsContractCreation && releaseSpec.IsEip3860Enabled) - { - dataCost += EvmPooledMemory.Div32Ceiling((UInt256)data.Length) * GasCostOf.InitCodeWord; - } - - return dataCost; - } - - private static long CalculateCalldataCost(Transaction transaction, IReleaseSpec releaseSpec) { long txDataNonZeroGasCost = releaseSpec.IsEip2028Enabled ? GasCostOf.TxDataNonZeroEip2028 : GasCostOf.TxDataNonZero; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 22b51495158..ed1e1ba7c8a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -3036,13 +3036,6 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref ReadOnlyMemory initCode = vmState.Memory.Load(in memoryPositionOfInitCode, initCodeLength); - if(initCode.Span.StartsWith(EvmObjectFormat.MAGIC)) - { - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - UInt256 balance = _state.GetBalance(env.ExecutingAccount); if (value > balance) { @@ -3079,7 +3072,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid // for another tx as returned to pool. - if (CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.HasEofMagic)) + if(spec.IsEofEnabled && initCode.Span.StartsWith(EvmObjectFormat.MAGIC)) { _returnDataBuffer = Array.Empty(); stack.PushZero(); @@ -3087,6 +3080,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } + CodeInfoFactory.CreateInitCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, out _); if (codeinfo is CodeInfo classicalCode) { classicalCode.AnalyseInBackgroundIfRequired(); From 522bff7b35c512232c8d5a423f0c56b50c917afe Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 2 Jul 2024 12:54:46 +0100 Subject: [PATCH 062/255] removing unused code --- .../Validators/TxValidator.cs | 2 +- src/Nethermind/Nethermind.Core/Transaction.cs | 1 + .../Nethermind.Evm/AddressExtensions.cs | 5 +-- .../Nethermind.Evm/IntrinsicGasCalculator.cs | 1 - .../Nethermind.Evm/TxExecutionContext.cs | 1 + .../Nethermind.Serialization.Rlp/TxDecoder.cs | 39 ------------------- 6 files changed, 5 insertions(+), 44 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index 4554062a513..bf37e38d874 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -150,7 +150,7 @@ private bool ValidateSignature(Transaction tx, IReleaseSpec spec) return !spec.ValidateChainId; } - private static bool Validate4844Fields(Transaction transaction, ref string error) + private static bool Validate4844Fields(Transaction transaction, ref string? error) { // Execution-payload version verification if (!transaction.SupportsBlobs) diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index 06fcbb7f6ce..1371eba41de 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -20,6 +20,7 @@ namespace Nethermind.Core public class Transaction { public const int BaseTxGasCost = 21000; + public ulong? ChainId { get; set; } /// diff --git a/src/Nethermind/Nethermind.Evm/AddressExtensions.cs b/src/Nethermind/Nethermind.Evm/AddressExtensions.cs index f6a13ba9ccd..ed886a60b5f 100644 --- a/src/Nethermind/Nethermind.Evm/AddressExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/AddressExtensions.cs @@ -30,13 +30,12 @@ public static Address From(Address deployingAddress, ReadOnlySpan salt, Re // sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code) ++ sha3(aux_data)) Span bytes = new byte[1 + Address.Size + Keccak.Size + salt.Length]; bytes[0] = 0xff; - deployingAddress.Bytes.CopyTo(bytes.Slice(1, 20)); + deployingAddress.Bytes.CopyTo(bytes.Slice(1, Address.Size)); salt.CopyTo(bytes.Slice(1 + Address.Size, salt.Length)); ValueKeccak.Compute(initCode).BytesAsSpan.CopyTo(bytes.Slice(1 + Address.Size + salt.Length, Keccak.Size)); ValueHash256 contractAddressKeccak = ValueKeccak.Compute(bytes); - Span addressBytes = contractAddressKeccak.BytesAsSpan[12..]; - return new Address(addressBytes.ToArray()); + return new Address(in contractAddressKeccak); } // See https://eips.ethereum.org/EIPS/eip-7610 diff --git a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs index f9ecf0c1a43..7742bcfc5ba 100644 --- a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs +++ b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs @@ -12,7 +12,6 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Nethermind.Core.Extensions; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Nethermind.Evm; diff --git a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs index 02ab0348d0f..637c6fc0258 100644 --- a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs +++ b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs @@ -12,6 +12,7 @@ public readonly struct TxExecutionContext public Address Origin { get; } public UInt256 GasPrice { get; } public byte[][]? BlobVersionedHashes { get; } + public TxExecutionContext(in BlockExecutionContext blockExecutionContext, Address origin, in UInt256 gasPrice, byte[][] blobVersionedHashes) { BlockExecutionContext = blockExecutionContext; diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs index 12548d15df7..66059c44de4 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs @@ -215,19 +215,6 @@ private void DecodeEip1559PayloadWithoutSig(T transaction, RlpStream rlpStream, transaction.AccessList = _accessListDecoder.Decode(rlpStream, rlpBehaviors); } - private void DecodeEofPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) - { - transaction.ChainId = rlpStream.DecodeULong(); - transaction.Nonce = rlpStream.DecodeUInt256(); - transaction.GasPrice = rlpStream.DecodeUInt256(); // gas premium - transaction.DecodedMaxFeePerGas = rlpStream.DecodeUInt256(); - transaction.GasLimit = rlpStream.DecodeLong(); - transaction.To = rlpStream.DecodeAddress(); - transaction.Value = rlpStream.DecodeUInt256(); - transaction.Data = rlpStream.DecodeByteArray(); - transaction.AccessList = _accessListDecoder.Decode(rlpStream, rlpBehaviors); - } - private void DecodeShardBlobPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) { transaction.ChainId = rlpStream.DecodeULong(); @@ -297,19 +284,6 @@ private void DecodeEip1559PayloadWithoutSig(T transaction, ref Rlp.ValueDecoderC transaction.Data = decoderContext.DecodeByteArrayMemory(); transaction.AccessList = _accessListDecoder.Decode(ref decoderContext, rlpBehaviors); } - private void DecodeEofPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) - { - transaction.ChainId = decoderContext.DecodeULong(); - transaction.Nonce = decoderContext.DecodeUInt256(); - transaction.GasPrice = decoderContext.DecodeUInt256(); // gas premium - transaction.DecodedMaxFeePerGas = decoderContext.DecodeUInt256(); - transaction.GasLimit = decoderContext.DecodeLong(); - transaction.To = decoderContext.DecodeAddress(); - transaction.Value = decoderContext.DecodeUInt256(); - transaction.Data = decoderContext.DecodeByteArrayMemory(); - transaction.AccessList = _accessListDecoder.Decode(ref decoderContext, rlpBehaviors); - } - private void DecodeShardBlobPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) { @@ -778,19 +752,6 @@ private int GetEip1559ContentLength(T item) + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None); } - private int GetEofContentLength(T item) - { - return Rlp.LengthOf(item.Nonce) - + Rlp.LengthOf(item.GasPrice) // gas premium - + Rlp.LengthOf(item.DecodedMaxFeePerGas) - + Rlp.LengthOf(item.GasLimit) - + Rlp.LengthOf(item.To) - + Rlp.LengthOf(item.Value) - + Rlp.LengthOf(item.Data) - + Rlp.LengthOf(item.ChainId ?? 0) - + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None); - } - private int GetShardBlobContentLength(T item) { return Rlp.LengthOf(item.Nonce) From 529ea099011f34244e72b014e590c9035f7beaba Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 2 Jul 2024 14:27:45 +0100 Subject: [PATCH 063/255] some refactor --- .../Nethermind.Evm/CodeInfoFactory.cs | 1 + .../Nethermind.Evm/CodeInfoRepository.cs | 5 ----- .../Nethermind.Evm/VirtualMachine.cs | 19 +++++++------------ 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index cecf57f0156..8c16d384006 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -21,6 +21,7 @@ public static bool CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, } return false; } + (codeinfo as CodeInfo).AnalyseInBackgroundIfRequired(); return true; } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 6e53d97315a..f33770738f5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -127,8 +127,6 @@ public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, I } CodeInfoFactory.CreateCodeInfo(code, vmSpec, out cachedCodeInfo, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); - if(cachedCodeInfo is CodeInfo eof0CodeInfo) - eof0CodeInfo.AnalyseInBackgroundIfRequired(); _codeCache.Set(codeHash, cachedCodeInfo); } else @@ -163,9 +161,6 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { CodeInfoFactory.CreateCodeInfo(code, spec, out ICodeInfo codeInfo, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); - if(codeInfo is CodeInfo eof0CodeInfo) - eof0CodeInfo.AnalyseInBackgroundIfRequired(); - Hash256 codeHash = code.Length == 0 ? Keccak.OfAnEmptyString : Keccak.Compute(code.Span); state.InsertCode(codeOwner, codeHash, code, spec); _codeCache.Set(codeHash, codeInfo); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index ed1e1ba7c8a..4964bb5359b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -404,21 +404,16 @@ public TransactionSubstate Run(EvmState state, IWorldState worl int containerIndex = callResult.Output.ContainerIndex.Value; ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; ReadOnlySpan container = previousState.Env.CodeInfo.ContainerSection(containerIndex).Span; - bool isEof_invalidated = !EvmObjectFormat.TryExtractHeader(container, out EofHeader? header) || header?.DataSection.Size != auxExtraData.Length; byte[] bytecodeResultArray = null; + Span bytecodeResult = new byte[container.Length + auxExtraData.Length]; - if (!isEof_invalidated) - { - Span bytecodeResult = new byte[container.Length + auxExtraData.Length]; - - // copy old container - container.CopyTo(bytecodeResult); - // copy aux data to dataSection - auxExtraData.CopyTo(bytecodeResult[container.Length..]); - bytecodeResultArray = bytecodeResult.ToArray(); - } + // copy old container + container.CopyTo(bytecodeResult); + // copy aux data to dataSection + auxExtraData.CopyTo(bytecodeResult[container.Length..]); + bytecodeResultArray = bytecodeResult.ToArray(); - bool invalidCode = !isEof_invalidated && !EvmObjectFormat.IsValidEof(bytecodeResultArray, EvmObjectFormat.ValidationStrategy.ValidateFullBody, out _) + bool invalidCode = !EvmObjectFormat.IsValidEof(bytecodeResultArray, EvmObjectFormat.ValidationStrategy.ValidateFullBody, out _) && bytecodeResultArray.Length < spec.MaxCodeSize; long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray?.Length ?? 0, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) From e5e88e10afd31140a55ba7dbeab63cd9bcbee7a0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 2 Jul 2024 16:17:11 +0100 Subject: [PATCH 064/255] Formatting --- .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 54 +++++++++++-------- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 5 +- .../Nethermind.Evm/CodeInfoFactory.cs | 8 +-- .../EvmObjectFormat/EofCodeInfo.cs | 2 +- .../EvmObjectFormat/EofCodeValidator.cs | 41 +++++++------- .../EvmObjectFormat/EofHeader.cs | 5 +- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 2 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 2 +- .../TransactionProcessor.cs | 15 ++++-- .../Nethermind.Evm/VirtualMachine.cs | 43 +++++++-------- 10 files changed, 100 insertions(+), 77 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 280391cdc1f..9af03112b6c 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -197,7 +197,7 @@ public void Test(long blockNumber, ulong? timestamp = null) .Op((byte)i) .Done; ; - if(InstructionExtensions.IsValid(opcode, true) && !InstructionExtensions.IsValid(opcode, false)) + if (InstructionExtensions.IsValid(opcode, true) && !InstructionExtensions.IsValid(opcode, false)) { var opcodeMetadata = InstructionExtensions.StackRequirements(opcode); opcodeMetadata.InputCount ??= 1; @@ -209,7 +209,7 @@ public void Test(long blockNumber, ulong? timestamp = null) List codesection = new(); - for(var j = 0; j < opcodeMetadata.InputCount; j++) + for (var j = 0; j < opcodeMetadata.InputCount; j++) { codesection.AddRange( Prepare.EvmCode @@ -222,7 +222,7 @@ public void Test(long blockNumber, ulong? timestamp = null) for (var j = 0; j < (opcodeMetadata.immediates ?? 3); j++) { - if(isFunCall && j == 1) + if (isFunCall && j == 1) { codesection.Add(1); continue; @@ -239,7 +239,7 @@ public void Test(long blockNumber, ulong? timestamp = null) ); } - if(opcode is not Instruction.JUMPF) + if (opcode is not Instruction.JUMPF) { codesection.Add((byte)Instruction.STOP); } @@ -248,36 +248,46 @@ public void Test(long blockNumber, ulong? timestamp = null) byte[] codeSectionSize = BitConverter.GetBytes((ushort)(codesection.Count)); code = [ // start header - 0xef, 0x00, + 0xef, + 0x00, 0x01, 0x01, - 0x00, (isFunCall ? (byte)0x08 : (byte)0x04), + 0x00, + (isFunCall ? (byte)0x08 : (byte)0x04), 0x02, - 0x00, (isFunCall ? (byte)0x02 : (byte)0x01), - codeSectionSize[1], codeSectionSize[0], - .. (isFunCall ? [0x00, 0x01] : Array.Empty()), + 0x00, + (isFunCall ? (byte)0x02 : (byte)0x01), + codeSectionSize[1], + codeSectionSize[0], + .. (isFunCall ? [0x00, 0x01] : Array.Empty()), 0x03, - 0x00, 0x01, - 0x00, 0x02, + 0x00, + 0x01, + 0x00, + 0x02, 0x04, - 0x00, 0x20, + 0x00, + 0x20, 0x00, // end header // start typesection - 0x00, 0x80, - stackHeighExpected[1], stackHeighExpected[0], - .. (isFunCall ? [0x00, 0x00, 0x00, 0x00] : Array.Empty()), + 0x00, + 0x80, + stackHeighExpected[1], + stackHeighExpected[0], + .. (isFunCall ? [0x00, 0x00, 0x00, 0x00] : Array.Empty()), // end typesection // start codesection - // start codesection 0 - .. codesection, - // end codesection 0 - // start codesection 1 - .. (isFunCall ? [(byte)Instruction.RETF]: Array.Empty()), - // end codesection 1 + // start codesection 0 + .. codesection, + // end codesection 0 + // start codesection 1 + .. (isFunCall ? [(byte)Instruction.RETF] : Array.Empty()), + // end codesection 1 // end codesection // start container section - (byte)Instruction.RETURNCONTRACT, 0x00, + (byte)Instruction.RETURNCONTRACT, + 0x00, // end container section // start data section .. Enumerable.Range(0, 32).Select(b => (byte)b).ToArray() diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index 4f288fa6ca3..87287965883 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -58,11 +58,12 @@ public static void HandleNumbits(int numbits, byte[] bitvec, scoped ref int pc) ushort setNBitsMask = (ushort)(~((1 << 32 - numbits) - 1)); - if(numbits > 1) + if (numbits > 1) { bitvec.SetN(pc, setNBitsMask); pc += numbits; - } else + } + else { bitvec.Set1(pc); pc += numbits; diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index 8c16d384006..def1b057892 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -14,7 +14,7 @@ public static bool CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, codeinfo = new CodeInfo(code); if (spec.IsEofEnabled && code.Span.StartsWith(EvmObjectFormat.MAGIC)) { - if(EvmObjectFormat.IsValidEof(code.Span, validationRules, out EofHeader? header)) + if (EvmObjectFormat.IsValidEof(code.Span, validationRules, out EofHeader? header)) { codeinfo = new EofCodeInfo(codeinfo, header.Value); return true; @@ -29,9 +29,9 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out { codeinfo = new CodeInfo(data); extraCalldata = default; - if(spec.IsEofEnabled && data.Span.StartsWith(EvmObjectFormat.MAGIC)) + if (spec.IsEofEnabled && data.Span.StartsWith(EvmObjectFormat.MAGIC)) { - if(EvmObjectFormat.IsValidEof(data.Span, EvmObjectFormat.ValidationStrategy.ValidateInitcodeMode | EvmObjectFormat.ValidationStrategy.ValidateFullBody | EvmObjectFormat.ValidationStrategy.ValidateSubContainers | EvmObjectFormat.ValidationStrategy.AllowTrailingBytes, out EofHeader? header)) + if (EvmObjectFormat.IsValidEof(data.Span, EvmObjectFormat.ValidationStrategy.ValidateInitcodeMode | EvmObjectFormat.ValidationStrategy.ValidateFullBody | EvmObjectFormat.ValidationStrategy.ValidateSubContainers | EvmObjectFormat.ValidationStrategy.AllowTrailingBytes, out EofHeader? header)) { int containerSize = header.Value.DataSection.EndOffset; extraCalldata = data.Slice(containerSize); @@ -39,6 +39,6 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out } return false; } - return true; + return true; } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index f1861bd294e..96a64e59f34 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -25,7 +25,7 @@ public class EofCodeInfo : ICodeInfo public ReadOnlyMemory ContainerSection(int index) { var offset = ContainerSectionOffset(index); - if (offset is null) + if (offset is null) return Memory.Empty; else { diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 1bc51922bfa..a14bbaf845e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -37,7 +37,8 @@ struct StackBounds() public short Max = -1; public short Min = 1023; - public void Combine(StackBounds other) { + public void Combine(StackBounds other) + { this.Max = Math.Max(this.Max, other.Max); this.Min = Math.Min(this.Min, other.Min); } @@ -91,29 +92,30 @@ static EvmObjectFormat() /// public static bool IsEof(ReadOnlySpan container, [NotNullWhen(true)] out int version) { - if(container.Length >= MAGIC.Length + 1) + if (container.Length >= MAGIC.Length + 1) { version = container[MAGIC.Length]; return container.StartsWith(MAGIC); - } else + } + else { version = 0; return false; } - + } public static bool IsEofn(ReadOnlySpan container, byte version) => container.Length >= MAGIC.Length + 1 && container.StartsWith(MAGIC) && container[MAGIC.Length] == version; public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy strategy, [NotNullWhen(true)] out EofHeader? header) { - if(strategy == ValidationStrategy.None) + if (strategy == ValidationStrategy.None) { header = null; return true; } - if(strategy.HasFlag(ValidationStrategy.HasEofMagic) && !container.StartsWith(MAGIC)) + if (strategy.HasFlag(ValidationStrategy.HasEofMagic) && !container.StartsWith(MAGIC)) { header = null; return false; @@ -376,14 +378,14 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => return true; } - public bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStrategy strategy) + public bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStrategy strategy) { int startOffset = header.TypeSection.Start; int endOffset = header.DataSection.Start; int calculatedCodeLength = header.TypeSection.Size - + header.CodeSections.Size - + (header.ContainerSection?.Size ?? 0); + + header.CodeSections.Size + + (header.ContainerSection?.Size ?? 0); CompoundSectionHeader codeSections = header.CodeSections; ReadOnlySpan contractBody = container[startOffset..endOffset]; ReadOnlySpan dataBody = container[endOffset..]; @@ -724,7 +726,7 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl } ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value.Start + header.ContainerSection.Value[initcodeSectionId].Start, header.ContainerSection.Value[initcodeSectionId].Size); - if(!IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody | ValidationStrategy.ValidateInitcodeMode, out _)) + if (!IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody | ValidationStrategy.ValidateInitcodeMode, out _)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must be a valid Eof"); return false; @@ -811,7 +813,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); targetMaxStackHeight = typesection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); - if(MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) + if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callf must not exceed {MAX_STACK_HEIGHT}"); return false; @@ -845,7 +847,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea return false; } - if(!opcode.IsTerminating()) + if (!opcode.IsTerminating()) { short delta = (short)(outputs - inputs); currentStackBounds.Max = (short)(currentStackBounds.Max + delta); @@ -870,15 +872,17 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); int jumpDestination = posPostInstruction + immediates.Value + offset; - if(opcode is Instruction.RJUMPI) + if (opcode is Instruction.RJUMPI) { recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); } - if(jumpDestination > programCounter) + if (jumpDestination > programCounter) { recordedStackHeight[jumpDestination].Combine(currentStackBounds); - } else { + } + else + { if (recordedStackHeight[jumpDestination] != currentStackBounds) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); @@ -912,7 +916,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } posPostInstruction += immediates.Value; - if(posPostInstruction > code.Length) + if (posPostInstruction > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); return false; @@ -926,11 +930,12 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (opcode.IsTerminating()) { - if(programCounter < code.Length) + if (programCounter < code.Length) { currentStackBounds = recordedStackHeight[programCounter]; } - } else + } + else { currentStackBounds.Combine(recordedStackHeight[programCounter]); recordedStackHeight[programCounter] = currentStackBounds; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index 924d3c91072..338324c5f63 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -34,12 +34,13 @@ private int[] SubSectionsSizesAcc { get { - if(subSectionsSizesAcc is null) { + if (subSectionsSizesAcc is null) + { subSectionsSizesAcc = new int[SubSectionsSizes.Length]; subSectionsSizesAcc[0] = 0; for (var i = 1; i < SubSectionsSizes.Length; i++) { - subSectionsSizesAcc[i] = subSectionsSizesAcc[i - 1] + SubSectionsSizes[i-1]; + subSectionsSizesAcc[i] = subSectionsSizesAcc[i - 1] + SubSectionsSizes[i - 1]; } } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index de3643d2d43..444cbf20bf3 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -11,7 +11,7 @@ namespace Nethermind.Evm.CodeAnalysis; public interface ICodeInfo { int Version => 0; - bool IsEmpty { get; } + bool IsEmpty { get; } ReadOnlyMemory MachineCode { get; } IPrecompile? Precompile { get; } bool IsPrecompile => Precompile is not null; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index bded8801d1e..1cf7ff09878 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -364,7 +364,7 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) spec ??= Frontier.Instance; return instruction switch { - Instruction.EXTCALL => "EXTCALL" , + Instruction.EXTCALL => "EXTCALL", Instruction.EXTSTATICCALL => "EXTSTATICCALL", // StaticCallEnabled Instruction.EXTDELEGATECALL => "EXTDELEGATECALL", Instruction.PREVRANDAO when !isPostMerge => "DIFFICULTY", diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 5c6fdeb4c14..a41a432107c 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -133,7 +133,8 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon if (commit) WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullTxTracer.Instance, commitStorageRoots: false); long gasAvailable = tx.GasLimit - intrinsicGas; - if(!(result = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, out ExecutionEnvironment? env))) { + if (!(result = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, out ExecutionEnvironment? env))) + { return result; } @@ -422,15 +423,19 @@ protected TransactionResult BuildExecutionEnvironment( byte[] inputData = tx.IsMessageCall ? tx.Data.AsArray() ?? Array.Empty() : Array.Empty(); if (tx.IsContractCreation) { - if(CodeInfoFactory.CreateInitCodeInfo(tx.Data ?? default, spec, out codeInfo, out Memory trailingData)) { + if (CodeInfoFactory.CreateInitCodeInfo(tx.Data ?? default, spec, out codeInfo, out Memory trailingData)) + { inputData = trailingData.ToArray(); - } else { + } + else + { return "Eip 7698: Invalid CreateTx Initcode"; } - } else + } + else { codeInfo = _codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec); - if(codeInfo is CodeInfo eofv0CodeInfo) + if (codeInfo is CodeInfo eofv0CodeInfo) eofv0CodeInfo.AnalyseInBackgroundIfRequired(); } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 4964bb5359b..6a2beece0f4 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -419,7 +419,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { ReadOnlyMemory code = callResult.Output.Bytes; - _codeInfoRepository.InsertCode(_state, code, callCodeOwner, spec); + _codeInfoRepository.InsertCode(_state, code, callCodeOwner, spec); currentState.GasAvailable -= codeDepositGasCost; if (_txTracer.IsTracingActions) @@ -1419,10 +1419,11 @@ private CallResult ExecuteCode externalCode = _codeInfoRepository.GetCachedCodeInfo(_state, address, spec).MachineCode; - if(spec.IsEofEnabled && EvmObjectFormat.IsEof(externalCode.Span, out _ )) + if (spec.IsEofEnabled && EvmObjectFormat.IsEof(externalCode.Span, out _)) { slice = EOF.EvmObjectFormat.MAGIC.SliceWithZeroPadding(0, 2); - } else + } + else { slice = externalCode.SliceWithZeroPadding(b, (int)result); } @@ -1458,7 +1459,7 @@ private CallResult ExecuteCode _returnDataBuffer.Length) + if (env.CodeInfo.Version == 0 && UInt256.AddOverflow(result, b, out c) || c > _returnDataBuffer.Length) { goto AccessViolation; } @@ -1872,7 +1873,7 @@ private CallResult ExecuteCode> 0x04); - int m = 1 + (int)(codeSection[programCounter] & 0x0f); + int m = 1 + (int)(codeSection[programCounter] & 0x0f); stack.Exchange(n, m); @@ -1925,7 +1926,7 @@ private CallResult ExecuteCode account = _state.GetCode(address); - if(spec.IsEofEnabled && EvmObjectFormat.IsEof(account, out _)) + if (spec.IsEofEnabled && EvmObjectFormat.IsEof(account, out _)) { stack.PushBytes(SHA256.HashData(EvmObjectFormat.MAGIC)); } @@ -2257,7 +2258,7 @@ private CallResult ExecuteCode( !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; ICodeInfo codeInfo = _codeInfoRepository.GetCachedCodeInfo(_state, codeSource, spec); - if(codeInfo is CodeInfo eof0CodeInfo) + if (codeInfo is CodeInfo eof0CodeInfo) eof0CodeInfo.AnalyseInBackgroundIfRequired(); if (spec.Use63Over64Rule) @@ -2657,7 +2658,7 @@ private EvmExceptionType InstructionEofCall targetBytes); stack.PopUInt256(out UInt256 dataOffset); stack.PopUInt256(out UInt256 dataLength); - + UInt256 callValue; switch (instruction) { @@ -2683,7 +2684,7 @@ private EvmExceptionType InstructionEofCall= long.MaxValue) return EvmExceptionType.OutOfGas; - if(!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; - if(callGas < GasCostOf.CallStipend || env.CallDepth >= MaxCallDepth || !transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) + if (callGas < GasCostOf.CallStipend || env.CallDepth >= MaxCallDepth || !transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) { _returnDataBuffer = Array.Empty(); stack.PushOne(); @@ -2857,7 +2858,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref } [SkipLocalsInit] - private (EvmExceptionType exceptionType, EvmState? callState) InstructionEofCreate(EvmState vmState, ref ReadOnlySpan codeSection, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) + private (EvmExceptionType exceptionType, EvmState? callState) InstructionEofCreate(EvmState vmState, ref ReadOnlySpan codeSection, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) where TTracing : struct, IIsTracing { ref readonly ExecutionEnvironment env = ref vmState.Env; @@ -2877,7 +2878,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref int initCodeIdx = codeSection[vmState.ProgramCounter++]; ReadOnlyMemory initCode = container.ContainerSection(initCodeIdx); int initcode_size = container.Header.ContainerSection.Value[initCodeIdx].Size; - + long gasCost = GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcode_size); @@ -3067,7 +3068,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid // for another tx as returned to pool. - if(spec.IsEofEnabled && initCode.Span.StartsWith(EvmObjectFormat.MAGIC)) + if (spec.IsEofEnabled && initCode.Span.StartsWith(EvmObjectFormat.MAGIC)) { _returnDataBuffer = Array.Empty(); stack.PushZero(); From 10ce03b968ef609b9c83247247c84246533fdb28 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 2 Jul 2024 23:34:54 +0100 Subject: [PATCH 065/255] Fix enablement bracketing logic --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 6a2beece0f4..8e5282f9ee1 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2268,9 +2268,6 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; @@ -2650,10 +2647,13 @@ private EvmExceptionType InstructionEofCall targetBytes); stack.PopUInt256(out UInt256 dataOffset); From b6283863ec1411eef657c6b74a6524600e1ef434 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 3 Jul 2024 00:40:06 +0100 Subject: [PATCH 066/255] Clean up, fix if bracketing --- .../Nethermind.Evm/VirtualMachine.cs | 74 ++++++++++--------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8e5282f9ee1..feec631ac81 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -4,8 +4,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Security.Cryptography; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; @@ -15,8 +19,7 @@ using Nethermind.Evm.Tracing; using Nethermind.Logging; using Nethermind.State; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Intrinsics; +using Nethermind.Evm.EOF; using static Nethermind.Evm.VirtualMachine; using static System.Runtime.CompilerServices.Unsafe; @@ -28,23 +31,7 @@ namespace Nethermind.Evm; -using System.Collections.Frozen; -using System.Linq; -using System.Runtime.InteropServices; -using System.Reflection.PortableExecutable; -using System.Security.Cryptography; -using DotNetty.Common.Utilities; -using System.Threading; - using Int256; -using Nethermind.Evm.EOF; -using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript; -using Org.BouncyCastle.Asn1.X509; -using SectionHeader = EOF.SectionHeader; - -using Nethermind.Core.Collections; -using System.Diagnostics; -using System.Runtime.Intrinsics.X86; public class VirtualMachine : IVirtualMachine { @@ -2646,6 +2633,8 @@ private EvmExceptionType InstructionEofCall targetBytes); stack.PopUInt256(out UInt256 dataOffset); stack.PopUInt256(out UInt256 dataLength); @@ -2668,58 +2658,74 @@ private EvmExceptionType InstructionEofCall= long.MaxValue) return EvmExceptionType.OutOfGas; - if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + // 8. Calculate the gas available to callee as caller’s remaining gas reduced by max(floor(gas/64), MIN_RETAINED_GAS). + long callGas = gasAvailable - Math.Max(gasAvailable / 64, MIN_RETAINED_GAS); - if (callGas < GasCostOf.CallStipend || env.CallDepth >= MaxCallDepth || !transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) + // 9. Fail with status code 1 returned on stack if any of the following is true (only gas charged until this point is consumed): + // a: Gas available to callee at this point is less than MIN_CALLEE_GAS. + // b: Balance of the current account is less than value. + // c: Current call stack depth equals 1024. + if (callGas < GasCostOf.CallStipend || + (!transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) || + env.CallDepth >= MaxCallDepth) { _returnDataBuffer = Array.Empty(); stack.PushOne(); + if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); if (typeof(TTracingInstructions) == typeof(IsTracing)) { // very specific for Parity trace, need to find generalization - very peculiar 32 length... ReadOnlyMemory memoryTrace = vmState.Memory.Inspect(in dataOffset, 32); _txTracer.ReportMemoryChange(dataOffset, memoryTrace.Span); + _txTracer.ReportOperationRemainingGas(gasAvailable); + _txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); + _txTracer.ReportGasUpdateForVmTrace(callGas, gasAvailable); } - if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationRemainingGas(gasAvailable); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); - - UpdateGasUp(callGas, ref gasAvailable); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportGasUpdateForVmTrace(callGas, gasAvailable); return EvmExceptionType.None; } + // 10. Perform the call with the available gas and configuration. + if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (typeof(TLogger) == typeof(IsTracing)) { _logger.Trace($"caller {caller}"); @@ -2733,7 +2739,9 @@ private EvmExceptionType InstructionEofCall(); + // 0 is success code should it be a failure code 2? stack.PushZero(); return EvmExceptionType.None; } @@ -2741,7 +2749,7 @@ private EvmExceptionType InstructionEofCall callData = vmState.Memory.Load(in dataOffset, dataLength); Snapshot snapshot = _state.TakeSnapshot(); - _state.SubtractFromBalance(caller, transferValue, spec); + if (!transferValue.IsZero) _state.SubtractFromBalance(caller, transferValue, spec); ExecutionEnvironment callEnv = new ( From b26fb9ab45e9baa7020617855ff97094ad342b06 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 3 Jul 2024 00:54:59 +0100 Subject: [PATCH 067/255] Return 1 for non-eof contract don't charge call gas --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index feec631ac81..5461f7d5f33 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2723,9 +2723,6 @@ private EvmExceptionType InstructionEofCall(); - // 0 is success code should it be a failure code 2? - stack.PushZero(); + stack.PushOne(); return EvmExceptionType.None; } + // 10. Perform the call with the available gas and configuration. + if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + ReadOnlyMemory callData = vmState.Memory.Load(in dataOffset, dataLength); Snapshot snapshot = _state.TakeSnapshot(); From 961609a0fdb502d5e9ba1c4d06a73e98e9391a85 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 3 Jul 2024 01:04:24 +0100 Subject: [PATCH 068/255] Only available in EOF --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 5461f7d5f33..1c0ed98943f 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2636,13 +2636,15 @@ private EvmExceptionType InstructionEofCall targetBytes); From 1d57470df0350150261ba99945bb8f631a6e9027 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 3 Jul 2024 02:51:43 +0100 Subject: [PATCH 069/255] Return statuscode should continue --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 1c0ed98943f..8004582b350 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2263,6 +2263,11 @@ private CallResult ExecuteCode= MaxCallDepth) { + returnData = CallResult.BoxedEmpty; _returnDataBuffer = Array.Empty(); stack.PushOne(); @@ -2743,6 +2749,7 @@ private EvmExceptionType InstructionEofCall(); stack.PushOne(); return EvmExceptionType.None; From a81516978df253ae9b63533d191f07ff9d8cf299 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 3 Jul 2024 05:39:53 +0100 Subject: [PATCH 070/255] Check stack returns --- .../Tracing/DebugTracerTests.cs | 2 +- src/Nethermind/Nethermind.Evm/EvmStack.cs | 6 +++-- .../Nethermind.Evm/VirtualMachine.cs | 24 +++++++++---------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs b/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs index 4a2a7140ee2..e42aeffa6d3 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs @@ -277,7 +277,7 @@ public void Debugger_Can_Alter_Data_Stack(string bytecodeHex) { // we pop the condition and overwrite it with a false to force breaking out of the loop EvmStack stack = new(tracer.CurrentState.DataStack, tracer.CurrentState.DataStackHead, tracer); - stack.PopLimbo(); + if (!stack.PopLimbo()) throw new EvmStackUnderflowException(); stack.PushByte(0x00); tracer.MoveNext(); diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 17ff90332fc..0050881b05a 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -219,12 +219,14 @@ public void PushSignedInt256(in Int256.Int256 value) PushUInt256(Unsafe.As(ref Unsafe.AsRef(in value))); } - public void PopLimbo() + public bool PopLimbo() { if (Head-- == 0) { - EvmStack.ThrowEvmStackUnderflowException(); + return false; } + + return true; } /// diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8004582b350..6d1886a4300 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1351,7 +1351,7 @@ private CallResult ExecuteCode= 256UL) { - stack.PopLimbo(); + if (!stack.PopLimbo()) goto StackUnderflow; stack.PushZero(); } else @@ -2002,7 +2002,7 @@ private CallResult ExecuteCode= 256) { - stack.PopLimbo(); + if (!stack.PopLimbo()) goto StackUnderflow; stack.PushZero(); } else @@ -2171,7 +2171,7 @@ private CallResult ExecuteCode 0) { if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; - stack.PopUInt256(out a); + if (!stack.PopUInt256(out a)) goto StackUnderflow; var count = codeSection[programCounter] + 1; var immediates = (ushort)(count * EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.ONE_BYTE_LENGTH); if (a < count) @@ -2279,8 +2279,8 @@ private CallResult ExecuteCode auxData = Span.Empty; if (b > UInt256.Zero) @@ -2308,7 +2308,7 @@ private CallResult ExecuteCode UInt256.Zero) { From 486e61955257c7f9f04def1032e6479203c9df1d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 3 Jul 2024 05:40:25 +0100 Subject: [PATCH 071/255] Handle ArrayPool arrays more safely --- .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 4 +- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 12 +- .../EvmObjectFormat/EofCodeInfo.cs | 1 - .../EvmObjectFormat/EofCodeValidator.cs | 324 ++++++++++-------- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 1 - 5 files changed, 183 insertions(+), 159 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 9af03112b6c..91482d7d713 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -195,9 +195,9 @@ public void Test(long blockNumber, ulong? timestamp = null) byte[] code = Prepare.EvmCode .Op((byte)i) - .Done; ; + .Done; - if (InstructionExtensions.IsValid(opcode, true) && !InstructionExtensions.IsValid(opcode, false)) + if (InstructionExtensions.IsValid(opcode, IsEofContext: true) && !InstructionExtensions.IsValid(opcode, IsEofContext: false)) { var opcodeMetadata = InstructionExtensions.StackRequirements(opcode); opcodeMetadata.InputCount ??= 1; diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index 87287965883..798baf291a0 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -39,7 +39,7 @@ public static byte[] CreateCodeBitmap(ReadOnlySpan code, bool isEof = fals return bitvec; } - public static void HandleNumbits(int numbits, byte[] bitvec, scoped ref int pc) + public static void HandleNumbits(int numbits, Span bitvec, scoped ref int pc) { if (numbits >= 8) { @@ -72,17 +72,17 @@ public static void HandleNumbits(int numbits, byte[] bitvec, scoped ref int pc) /// /// Checks if the position is in a code segment. /// - public static bool IsCodeSegment(byte[] bitvec, int pos) + public static bool IsCodeSegment(Span bitvec, int pos) { return (bitvec[pos / 8] & (0x80 >> (pos % 8))) == 0; } - private static void Set1(this byte[] bitvec, int pos) + private static void Set1(this Span bitvec, int pos) { bitvec[pos / 8] |= _lookup[pos % 8]; } - private static void SetN(this byte[] bitvec, int pos, UInt16 flag) + private static void SetN(this Span bitvec, int pos, UInt16 flag) { ushort a = (ushort)(flag >> (pos % 8)); bitvec[pos / 8] |= (byte)(a >> 8); @@ -95,14 +95,14 @@ private static void SetN(this byte[] bitvec, int pos, UInt16 flag) } } - private static void Set8(this byte[] bitvec, int pos) + private static void Set8(this Span bitvec, int pos) { byte a = (byte)(0xFF >> (pos % 8)); bitvec[pos / 8] |= a; bitvec[pos / 8 + 1] = (byte)~a; } - private static void Set16(this byte[] bitvec, int pos) + private static void Set16(this Span bitvec, int pos) { byte a = (byte)(0xFF >> (pos % 8)); bitvec[pos / 8] |= a; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index 96a64e59f34..a745a200f35 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -5,7 +5,6 @@ using Nethermind.Core.Extensions; using Nethermind.Evm.EOF; using Nethermind.Evm.Precompiles; -using static System.Collections.Specialized.BitVector32; namespace Nethermind.Evm.CodeAnalysis; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index a14bbaf845e..580b175ac43 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -435,7 +435,10 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat return false; } - bool[] visitedSections = ArrayPool.Shared.Rent(header.CodeSections.Count); + bool[] visitedSectionsArray = ArrayPool.Shared.Rent(header.CodeSections.Count); + Span visitedSections = visitedSectionsArray.AsSpan(0, header.CodeSections.Count); + visitedSections.Clear(); + Queue validationQueue = new Queue(); validationQueue.Enqueue(0); @@ -453,14 +456,22 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSectionStartOffset, codeSectionSize); if (!ValidateInstructions(sectionIdx, strategy, typesection, code, header, container, validationQueue)) { - ArrayPool.Shared.Return(visitedSections, true); + ArrayPool.Shared.Return(visitedSectionsArray); return false; } } - var HasNoNonReachableCodeSections = visitedSections[..header.CodeSections.Count].All(id => id); - ArrayPool.Shared.Return(visitedSections, true); + bool HasNoNonReachableCodeSections = false; + for (int i = 0; i < header.CodeSections.Count; i++) + { + if (!visitedSections[i]) + { + HasNoNonReachableCodeSections = true; + break; + } + } + ArrayPool.Shared.Return(visitedSectionsArray); return HasNoNonReachableCodeSections; } @@ -507,12 +518,19 @@ bool ValidateTypeSection(ReadOnlySpan types) bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist) { - byte[] codeBitmap = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); - byte[] jumpdests = ArrayPool.Shared.Rent((code.Length / BYTE_BIT_COUNT) + 1); + int length = (code.Length / BYTE_BIT_COUNT) + 1; + byte[] codeBitmapArray = ArrayPool.Shared.Rent(length); + byte[] jumpDestsArray = ArrayPool.Shared.Rent(length); try { - int pos; + // ArrayPool may return a larger array than requested, so we need to slice it to the actual length + Span codeBitmap = codeBitmapArray.AsSpan(0, length); + Span jumpDests = jumpDestsArray.AsSpan(0, length); + // ArrayPool may return a larger array than requested, so we need to slice it to the actual length + codeBitmap.Clear(); + jumpDests.Clear(); + int pos; for (pos = 0; pos < code.Length;) { Instruction opcode = (Instruction)code[pos]; @@ -551,7 +569,7 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl return false; } - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpdests, ref rjumpdest); + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpDests, ref rjumpdest); BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); } @@ -627,7 +645,7 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Destination outside of Code bounds"); return false; } - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpdests, ref rjumpdest); + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpDests, ref rjumpdest); } BitmapHelper.HandleNumbits(immediateValueSize, codeBitmap, ref postInstructionByte); } @@ -753,7 +771,7 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl return false; } - bool result = !BitmapHelper.CheckCollision(codeBitmap, jumpdests); + bool result = !BitmapHelper.CheckCollision(codeBitmap, jumpDests); if (!result) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination"); @@ -767,140 +785,119 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl } finally { - ArrayPool.Shared.Return(codeBitmap, true); - ArrayPool.Shared.Return(jumpdests, true); + ArrayPool.Shared.Return(codeBitmapArray); + ArrayPool.Shared.Return(jumpDestsArray); } } public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection) { - StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); - Array.Fill(recordedStackHeight, new StackBounds()); - - ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); - - ushort currrentSectionOutputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - short peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + StackBounds[] recordedStackHeightArray = ArrayPool.Shared.Rent(code.Length); + try + { + Span recordedStackHeight = recordedStackHeightArray.AsSpan(0, code.Length); + recordedStackHeight.Clear(); - int unreachedBytes = code.Length; - bool isTargetSectionNonReturning = false; + ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); - int targetMaxStackHeight = 0; - int programCounter = 0; - recordedStackHeight[0].Max = peakStackHeight; - recordedStackHeight[0].Min = peakStackHeight; - StackBounds currentStackBounds = recordedStackHeight[0]; + ushort currrentSectionOutputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + short peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - while (programCounter < code.Length) - { - Instruction opcode = (Instruction)code[programCounter]; - (ushort? inputs, ushort? outputs, ushort? immediates) = opcode.StackRequirements(); + int unreachedBytes = code.Length; + bool isTargetSectionNonReturning = false; - ushort posPostInstruction = (ushort)(programCounter + 1); - if (posPostInstruction > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; - } + int targetMaxStackHeight = 0; + int programCounter = 0; + recordedStackHeight[0].Max = peakStackHeight; + recordedStackHeight[0].Min = peakStackHeight; + StackBounds currentStackBounds = recordedStackHeight[0]; - switch (opcode) + while (programCounter < code.Length) { - case Instruction.CALLF or Instruction.JUMPF: - ushort targetSectionId = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); - inputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - - outputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; - outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); - targetMaxStackHeight = typesection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); + Instruction opcode = (Instruction)code[programCounter]; + (ushort? inputs, ushort? outputs, ushort? immediates) = opcode.StackRequirements(); - if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callf must not exceed {MAX_STACK_HEIGHT}"); - return false; - } - - if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currrentSectionOutputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currrentSectionOutputs + inputs - outputs} but found {currentStackBounds.Max}"); - return false; - } - break; - case Instruction.DUPN: - byte imm = code[posPostInstruction]; - inputs = (ushort)(imm + 1); - outputs = (ushort)(inputs + 1); - break; - case Instruction.SWAPN: - imm = code[posPostInstruction]; - outputs = inputs = (ushort)(2 + imm); - break; - case Instruction.EXCHANGE: - byte imm_n = (byte)(code[posPostInstruction] >> 4); - byte imm_m = (byte)(code[posPostInstruction] & 0x0F); - outputs = inputs = (ushort)(imm_n + imm_m + 3); - break; - } + ushort posPostInstruction = (ushort)(programCounter + 1); + if (posPostInstruction > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); + return false; + } - if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputs) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputs} but found {currentStackBounds.Min}"); - return false; - } + switch (opcode) + { + case Instruction.CALLF or Instruction.JUMPF: + ushort targetSectionId = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); + inputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - if (!opcode.IsTerminating()) - { - short delta = (short)(outputs - inputs); - currentStackBounds.Max = (short)(currentStackBounds.Max + delta); - currentStackBounds.Min = (short)(currentStackBounds.Min + delta); - } - peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); + outputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; + outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); + targetMaxStackHeight = typesection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); - switch (opcode) - { - case Instruction.RETF: - { - var expectedHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - if (expectedHeight != currentStackBounds.Min || !currentStackBounds.BoundsEqual()) + if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid required height {expectedHeight} but found {currentStackBounds.Min}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callf must not exceed {MAX_STACK_HEIGHT}"); return false; } - break; - } - case Instruction.RJUMP or Instruction.RJUMPI: - { - short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); - int jumpDestination = posPostInstruction + immediates.Value + offset; - if (opcode is Instruction.RJUMPI) + if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currrentSectionOutputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) { - recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currrentSectionOutputs + inputs - outputs} but found {currentStackBounds.Max}"); + return false; } + break; + case Instruction.DUPN: + byte imm = code[posPostInstruction]; + inputs = (ushort)(imm + 1); + outputs = (ushort)(inputs + 1); + break; + case Instruction.SWAPN: + imm = code[posPostInstruction]; + outputs = inputs = (ushort)(2 + imm); + break; + case Instruction.EXCHANGE: + byte imm_n = (byte)(code[posPostInstruction] >> 4); + byte imm_m = (byte)(code[posPostInstruction] & 0x0F); + outputs = inputs = (ushort)(imm_n + imm_m + 3); + break; + } - if (jumpDestination > programCounter) - { - recordedStackHeight[jumpDestination].Combine(currentStackBounds); - } - else + if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputs) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputs} but found {currentStackBounds.Min}"); + return false; + } + + if (!opcode.IsTerminating()) + { + short delta = (short)(outputs - inputs); + currentStackBounds.Max = (short)(currentStackBounds.Max + delta); + currentStackBounds.Min = (short)(currentStackBounds.Min + delta); + } + peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); + + switch (opcode) + { + case Instruction.RETF: { - if (recordedStackHeight[jumpDestination] != currentStackBounds) + var 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 at {jumpDestination}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid required height {expectedHeight} but found {currentStackBounds.Min}"); return false; } + break; } - - break; - } - case Instruction.RJUMPV: - { - var count = code[posPostInstruction] + 1; - immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); - for (short j = 0; j < count; j++) + case Instruction.RJUMP or Instruction.RJUMPI: { - int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; - int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); + short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); int jumpDestination = posPostInstruction + immediates.Value + offset; + + if (opcode is Instruction.RJUMPI) + { + recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); + } + if (jumpDestination > programCounter) { recordedStackHeight[jumpDestination].Combine(currentStackBounds); @@ -913,54 +910,83 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea return false; } } - } - posPostInstruction += immediates.Value; - if (posPostInstruction > code.Length) + break; + } + case Instruction.RJUMPV: { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; + var count = code[posPostInstruction] + 1; + immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); + for (short j = 0; j < count; j++) + { + int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; + int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); + int jumpDestination = posPostInstruction + immediates.Value + offset; + if (jumpDestination > programCounter) + { + recordedStackHeight[jumpDestination].Combine(currentStackBounds); + } + else + { + if (recordedStackHeight[jumpDestination] != currentStackBounds) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); + return false; + } + } + } + + posPostInstruction += immediates.Value; + if (posPostInstruction > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); + return false; + } + break; } - break; - } - } + } - unreachedBytes -= 1 + immediates.Value; - programCounter += 1 + immediates.Value; + unreachedBytes -= 1 + immediates.Value; + programCounter += 1 + immediates.Value; - if (opcode.IsTerminating()) - { - if (programCounter < code.Length) + if (opcode.IsTerminating()) + { + if (programCounter < code.Length) + { + currentStackBounds = recordedStackHeight[programCounter]; + } + } + else { - currentStackBounds = recordedStackHeight[programCounter]; + currentStackBounds.Combine(recordedStackHeight[programCounter]); + recordedStackHeight[programCounter] = currentStackBounds; } } - else + + if (unreachedBytes != 0) { - currentStackBounds.Combine(recordedStackHeight[programCounter]); - recordedStackHeight[programCounter] = currentStackBounds; + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, bytecode has unreachable segments"); + return false; } - } - 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 != suggestedMaxHeight) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); - return false; + bool result = peakStackHeight < MAX_STACK_HEIGHT; + if (!result) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); + return false; + } + return result; } - - bool result = peakStackHeight < MAX_STACK_HEIGHT; - if (!result) + finally { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); - return false; + ArrayPool.Shared.Return(recordedStackHeightArray); } - return result; } } } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index 444cbf20bf3..8a9a245eb42 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Diagnostics; using Nethermind.Evm.EOF; using Nethermind.Evm.Precompiles; From b4f147537c3d287d2de5e2bd1f80a4ccfba405fb Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 3 Jul 2024 06:13:06 +0100 Subject: [PATCH 072/255] Fix out of bounds vector access --- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index 798baf291a0..c9f62ac9ded 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -110,50 +110,41 @@ private static void Set16(this Span bitvec, int pos) bitvec[pos / 8 + 2] = (byte)~a; } - private const uint Vector128ByteCount = 16; - private const uint Vector128IntCount = 4; - private const uint Vector256ByteCount = 32; - private const uint Vector256IntCount = 8; - public static bool CheckCollision(ReadOnlySpan codeSegments, ReadOnlySpan jumpmask) { int count = Math.Min(codeSegments.Length, jumpmask.Length); - uint i = 0; + int i = 0; ref byte left = ref MemoryMarshal.GetReference(codeSegments); ref byte right = ref MemoryMarshal.GetReference(jumpmask); - if (Vector256.IsHardwareAccelerated) + if (Vector256.IsHardwareAccelerated && count >= Vector256.Count) { - Vector256 zeros = Vector256.Create(0); - for (; i < (uint)count - (Vector256IntCount - 1u); i += Vector256IntCount) + for (; (uint)(i + Vector256.Count) <= (uint)count; i += Vector256.Count) { - Vector256 result = Vector256.LoadUnsafe(ref left, i) & Vector256.LoadUnsafe(ref right, i); - result = Vector256.Min(result, zeros); - if (Vector256.Sum(result) != 0) + Vector256 result = Vector256.LoadUnsafe(ref left, (uint)i) & Vector256.LoadUnsafe(ref right, (uint)i); + if (result != default) { return true; } } } - else if (Vector128.IsHardwareAccelerated) + else if (Vector128.IsHardwareAccelerated && count >= Vector128.Count) { - Vector128 zeros = Vector128.Create(0); - for (; i < (uint)count - (Vector128IntCount - 1u); i += Vector128IntCount) + for (; (i + Vector128.Count) <= (uint)count; i += Vector128.Count) { - Vector128 result = Vector128.LoadUnsafe(ref left, i) & Vector128.LoadUnsafe(ref right, i); - result = Vector128.Min(result, zeros); - if (Vector128.Sum(result) != 0) + Vector128 result = Vector128.LoadUnsafe(ref left, (uint)i) & Vector128.LoadUnsafe(ref right, (uint)i); + if (result != default) { return true; } } } - for (int j = (int)i; j < (uint)count; j++) + for (; i < count; i++) { - if ((codeSegments[j] & jumpmask[j]) != 0) + if ((codeSegments[i] & jumpmask[i]) != 0) { return true; } From b037ba828678959e9b524561f21914ac77d4f04b Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 3 Jul 2024 16:30:09 +0100 Subject: [PATCH 073/255] Added comments, and fixed bugs --- src/Nethermind/Nethermind.Evm/EvmException.cs | 3 +- .../EvmObjectFormat/EofCodeInfo.cs | 2 +- .../EvmObjectFormat/EofCodeValidator.cs | 23 ++-- .../EvmObjectFormat/EofHeader.cs | 22 +-- .../Nethermind.Evm/VirtualMachine.cs | 129 +++++++++++------- 5 files changed, 108 insertions(+), 71 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmException.cs b/src/Nethermind/Nethermind.Evm/EvmException.cs index a1c76b8390f..4f00dc5a3ca 100644 --- a/src/Nethermind/Nethermind.Evm/EvmException.cs +++ b/src/Nethermind/Nethermind.Evm/EvmException.cs @@ -29,6 +29,7 @@ public enum EvmExceptionType NotEnoughBalance, Other, Revert, - InvalidCode + InvalidCode, + DataSectionIndexOutOfRange } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index a745a200f35..44060fcdf56 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -53,7 +53,7 @@ public EofCodeInfo(ICodeInfo codeInfo, in EofHeader header) _codeInfo = codeInfo; Header = header; TypeSection = MachineCode.Slice(Header.TypeSection.Start, Header.TypeSection.Size); - DataSection = MachineCode.Slice(Header.DataSection.Start, Header.DataSection.Size); CodeSection = MachineCode.Slice(Header.CodeSections.Start, Header.CodeSections.Size); + DataSection = MachineCode.Slice(Header.DataSection.Start); } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 580b175ac43..161414cf140 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -345,24 +345,24 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => SectionHeader typeSectionHeader = new ( - Start: HEADER_TERMINATOR_OFFSET + ONE_BYTE_LENGTH, - Size: sectionSizes.TypeSectionSize + start: HEADER_TERMINATOR_OFFSET + ONE_BYTE_LENGTH, + size: sectionSizes.TypeSectionSize ); CompoundSectionHeader codeSectionHeader = new( - Start: typeSectionHeader.EndOffset, - SubSectionsSizes: codeSections + start: typeSectionHeader.EndOffset, + subSectionsSizes: codeSections ); CompoundSectionHeader? containerSectionHeader = containerSections is null ? null : new( - Start: codeSectionHeader.EndOffset, - SubSectionsSizes: containerSections + start: codeSectionHeader.EndOffset, + subSectionsSizes: containerSections ); SectionHeader dataSectionHeader = new( - Start: containerSectionHeader?.EndOffset ?? codeSectionHeader.EndOffset, - Size: sectionSizes.DataSectionSize + start: containerSectionHeader?.EndOffset ?? codeSectionHeader.EndOffset, + size: sectionSizes.DataSectionSize ); header = new EofHeader @@ -389,7 +389,8 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat CompoundSectionHeader codeSections = header.CodeSections; ReadOnlySpan contractBody = container[startOffset..endOffset]; ReadOnlySpan dataBody = container[endOffset..]; - (int typeSectionStart, ushort typeSectionSize) = header.TypeSection; + var typeSection = header.TypeSection; + (int typeSectionStart, int typeSectionSize) = (typeSection.Start, typeSection.Size); if (header.ContainerSection?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) { @@ -451,9 +452,9 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat } visitedSections[sectionIdx] = true; - (int codeSectionStartOffset, int codeSectionSize) = header.CodeSections[sectionIdx]; + var codeSection = header.CodeSections[sectionIdx]; - ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSectionStartOffset, codeSectionSize); + ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSection.Start, codeSection.Size); if (!ValidateInstructions(sectionIdx, strategy, typesection, code, header, container, validationQueue)) { ArrayPool.Shared.Return(visitedSectionsArray); diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index 338324c5f63..ae21551bad5 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -14,20 +14,26 @@ public struct EofHeader() public required int PrefixSize; public required SectionHeader TypeSection; public required CompoundSectionHeader CodeSections; - public required SectionHeader DataSection; public required CompoundSectionHeader? ContainerSection; + public required SectionHeader DataSection; } -public record struct SectionHeader(int Start, ushort Size) +public struct SectionHeader(int start, ushort size) { - public int EndOffset => Start + Size; + public readonly int Start => start; + public readonly int Size => size; + public readonly int EndOffset => Start + Size; } -public record struct CompoundSectionHeader(int Start, int[] SubSectionsSizes) +public struct CompoundSectionHeader(int start, int[] subSectionsSizes) { - public readonly int EndOffset = Start + SubSectionsSizes.Sum(); - public int Size => EndOffset - Start; - public int Count => SubSectionsSizes.Length; + 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 int[] subSectionsSizesAcc; private int[] SubSectionsSizesAcc @@ -48,5 +54,5 @@ private int[] SubSectionsSizesAcc } } - public SectionHeader this[int i] => new SectionHeader(Start: SubSectionsSizesAcc[i], Size: (ushort)SubSectionsSizes[i]); + public SectionHeader this[int i] => new SectionHeader(SubSectionsSizesAcc[i], (ushort)SubSectionsSizes[i]); } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 6d1886a4300..4f0731a5c34 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -388,23 +388,44 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } else if (previousState.ExecutionType.IsAnyCreateEof()) { - int containerIndex = callResult.Output.ContainerIndex.Value; + // ReturnContract was called with a container index and auxdata + + EofCodeInfo eofCodeInfo = (EofCodeInfo)previousState.Env.CodeInfo; + + // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed + int deployContainerIndex = callResult.Output.ContainerIndex.Value; ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; - ReadOnlySpan container = previousState.Env.CodeInfo.ContainerSection(containerIndex).Span; + ReadOnlySpan container = eofCodeInfo.ContainerSection(deployContainerIndex).Span; byte[] bytecodeResultArray = null; - Span bytecodeResult = new byte[container.Length + auxExtraData.Length]; - // copy old 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[container.Length + auxExtraData.Length]; + // 2 - 1 - 1 - copy old container container.CopyTo(bytecodeResult); - // copy aux data to dataSection + // 2 - 1 - 2 - copy aux data to dataSection auxExtraData.CopyTo(bytecodeResult[container.Length..]); + + // 2 - 2 - update data section size in the header u16 + int dataSubheaderSectionStart = + EvmObjectFormat.VERSION_OFFSET // magic + version + + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) + + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) + + (eofCodeInfo.Header.ContainerSection is null + ? 0 // container section : (0 bytes if no container section is available) + : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.ContainerSection.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator + bytecodeResult[dataSubheaderSectionStart] = (byte)(bytecodeResult.Length >> 8); + bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(bytecodeResult.Length & 0xFF); + bytecodeResultArray = bytecodeResult.ToArray(); - bool invalidCode = !EvmObjectFormat.IsValidEof(bytecodeResultArray, EvmObjectFormat.ValidationStrategy.ValidateFullBody, out _) - && bytecodeResultArray.Length < spec.MaxCodeSize; + // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts + bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray?.Length ?? 0, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { + // 4 - set state[new_address].code to the updated deploy container + // push new_address onto the stack (already done before the ifs) ReadOnlyMemory code = callResult.Output.Bytes; _codeInfoRepository.InsertCode(_state, code, callCodeOwner, spec); currentState.GasAvailable -= codeDepositGasCost; @@ -2233,7 +2254,7 @@ private CallResult ExecuteCode auxData = Span.Empty; + ReadOnlyMemory auxData = ReadOnlyMemory.Empty; if (b > UInt256.Zero) { + if(dataSection.Length + (int)b > (env.CodeInfo as EofCodeInfo).Header.DataSection.Size) + { + goto DataSectionAccessViolation; + } + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; - auxData = vmState.Memory.LoadSpan(a, b); + auxData = vmState.Memory.Load(a, b); } - return new CallResult(sectionIdx, auxData.ToArray(), null, env.CodeInfo.Version); + return new CallResult(sectionIdx, auxData, null, env.CodeInfo.Version); } case Instruction.DATASIZE: { @@ -2412,6 +2438,9 @@ private CallResult ExecuteCode(gasAvailable, exceptionType); InvalidSubroutineEntry: @@ -2884,45 +2913,52 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref ref readonly ExecutionEnvironment env = ref vmState.Env; EofCodeInfo container = env.CodeInfo as EofCodeInfo; var currentContext = ExecutionType.EOFCREATE; + + // 1 - deduct TX_CREATE_COST gas if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - if (!stack.PopUInt256(out UInt256 value) || - !stack.PopWord256(out Span salt) || - !stack.PopUInt256(out UInt256 dataOffset) || - !stack.PopUInt256(out UInt256 dataSize)) - return (EvmExceptionType.StackUnderflow, null); - - if (!UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataSize)) return (EvmExceptionType.OutOfGas, null); + // 2 - read immediate operand initcontainer_index, encoded as 8-bit unsigned value + int initcontainerIndex = codeSection[vmState.ProgramCounter++]; - int initCodeIdx = codeSection[vmState.ProgramCounter++]; - ReadOnlyMemory initCode = container.ContainerSection(initCodeIdx); - int initcode_size = container.Header.ContainerSection.Value[initCodeIdx].Size; + // 3 - pop value, salt, input_offset, input_size from the operand stack + // no stack checks becaue EOF guarantees no stack undeflows + stack.PopUInt256(out UInt256 value); + stack.PopWord256(out Span salt); + stack.PopUInt256(out UInt256 dataOffset) ; + stack.PopUInt256(out UInt256 dataSize); + // 4 - perform (and charge for) memory expansion using [input_offset, input_size] + if (!UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataSize)) return (EvmExceptionType.OutOfGas, null); - long gasCost = GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcode_size); - - if (!UpdateGas(gasCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); + // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed + // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header + ReadOnlyMemory initcontainer = container.ContainerSection(initcontainerIndex); + int initcontainerSize = container.Header.ContainerSection.Value[initcontainerIndex].Size; - // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? - if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients - { - // TODO: need a test for this - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } + // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) + if (!UpdateGas(GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize), ref gasAvailable)) + return (EvmExceptionType.OutOfGas, null); + // 7 - check that current call depth is below STACK_DEPTH_LIMIT and that caller balance is enough to transfer value + // in case of failure return 0 on the stack, caller’s nonce is not updated and gas for initcode execution is not consumed. UInt256 balance = _state.GetBalance(env.ExecutingAccount); - if (value > balance) + if (env.CallDepth >= MaxCallDepth || value > balance) { + // TODO: need a test for this _returnDataBuffer = Array.Empty(); stack.PushZero(); return (EvmExceptionType.None, null); } + // 8 - caller’s memory slice [input_offset:input_size] is used as calldata Span calldata = vmState.Memory.LoadSpan(dataOffset, dataSize); + // 9 - execute the container and deduct gas for execution. The 63/64th rule from EIP-150 applies. + long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; + if (!UpdateGas(callGas, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); + + // 10 - increment sender account’s nonce UInt256 accountNonce = _state.GetNonce(env.ExecutingAccount); UInt256 maxNonce = ulong.MaxValue; if (accountNonce >= maxNonce) @@ -2932,27 +2968,22 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } - if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState?.Memory.Size ?? 0); - // todo: === below is a new call - refactor / move - - long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; - if (!UpdateGas(callGas, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - - Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initCode.Span); - + // 11 - calculate new_address as keccak256(0xff || sender || salt || keccak256(initcontainer))[12:] + Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initcontainer.Span); if (spec.UseHotAndColdStorage) { // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 vmState.WarmUp(contractAddress); } - _state.IncrementNonce(env.ExecutingAccount); + if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState?.Memory.Size ?? 0); + // todo: === below is a new call - refactor / move Snapshot snapshot = _state.TakeSnapshot(); bool accountExists = _state.AccountExists(contractAddress); - if (accountExists && (_codeInfoRepository.GetCachedCodeInfo(_state, contractAddress, spec).MachineCode.Length != 0 || - _state.GetNonce(contractAddress) != 0)) + + if (accountExists && contractAddress.IsNonZeroAccount(spec, _codeInfoRepository, _state)) { /* we get the snapshot before this as there is a possibility with that we will touch an empty account and remove it even if the REVERT operation follows */ if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); @@ -2961,18 +2992,16 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } - if (accountExists) - { - _state.UpdateStorageRoot(contractAddress, Keccak.EmptyTreeHash); - } - else if (_state.IsDeadAccount(contractAddress)) + if (_state.IsDeadAccount(contractAddress)) { _state.ClearStorage(contractAddress); } _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.ExractHeader); + _state.IncrementNonce(env.ExecutingAccount); + + CodeInfoFactory.CreateCodeInfo(initcontainer.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.ExractHeader); ExecutionEnvironment callEnv = new ( From 2765ac0e5322431cd0f6fc3066e48c8a53da51d8 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 3 Jul 2024 17:51:47 +0100 Subject: [PATCH 074/255] fix unreachable code check --- .../EvmObjectFormat/EofCodeValidator.cs | 20 +++++++++---------- .../Nethermind.Evm/VirtualMachine.cs | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 161414cf140..5cf22b2ca62 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -9,6 +9,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using DotNetty.Common.Utilities; using FastEnumUtility; using Nethermind.Core.Extensions; using Nethermind.Logging; @@ -473,7 +474,7 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat } ArrayPool.Shared.Return(visitedSectionsArray); - return HasNoNonReachableCodeSections; + return !HasNoNonReachableCodeSections; } bool ValidateTypeSection(ReadOnlySpan types) @@ -792,12 +793,11 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl } public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection) { - StackBounds[] recordedStackHeightArray = ArrayPool.Shared.Rent(code.Length); + StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); + recordedStackHeight.Fill(new StackBounds()); + try { - Span recordedStackHeight = recordedStackHeightArray.AsSpan(0, code.Length); - recordedStackHeight.Clear(); - ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); ushort currrentSectionOutputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; @@ -872,8 +872,8 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (!opcode.IsTerminating()) { short delta = (short)(outputs - inputs); - currentStackBounds.Max = (short)(currentStackBounds.Max + delta); - currentStackBounds.Min = (short)(currentStackBounds.Min + delta); + currentStackBounds.Max += delta; + currentStackBounds.Min += delta; } peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); @@ -959,8 +959,8 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } else { - currentStackBounds.Combine(recordedStackHeight[programCounter]); - recordedStackHeight[programCounter] = currentStackBounds; + recordedStackHeight[programCounter].Combine(currentStackBounds); + currentStackBounds = recordedStackHeight[programCounter]; } } @@ -986,7 +986,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } finally { - ArrayPool.Shared.Return(recordedStackHeightArray); + ArrayPool.Shared.Return(recordedStackHeight); } } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 4f0731a5c34..d310ab1cb7f 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -419,6 +419,8 @@ public TransactionSubstate Run(EvmState state, IWorldState worl bytecodeResultArray = bytecodeResult.ToArray(); + int dataSize = bytecodeResult[dataSubheaderSectionStart..].ReadEthUInt16(); + // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray?.Length ?? 0, spec); From 68cfba35c1b2d7d74a6db15637025eebf0c8e873 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 4 Jul 2024 01:04:41 +0100 Subject: [PATCH 075/255] Add more validation rules for EOFCREATE and RETURNCONTRACT --- .../Nethermind.Evm/CodeDepositHandler.cs | 6 +- .../EvmObjectFormat/EofCodeInfo.cs | 6 +- .../EvmObjectFormat/EofCodeValidator.cs | 81 +++++++++---------- .../EvmObjectFormat/EofHeader.cs | 2 +- .../Nethermind.Evm/VirtualMachine.cs | 10 ++- 5 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs index 5b8f597113e..cf7d1ea5adb 100644 --- a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs @@ -47,9 +47,9 @@ public static bool IsValidWithEofRules(ReadOnlySpan code, int fromVersion, bool isCodeEof = EvmObjectFormat.IsEof(code, out int codeVersion); bool valid = code.Length >= 1 && codeVersion >= fromVersion - && (isCodeEof ? // this needs test cases - EvmObjectFormat.IsValidEof(code, strategy, out _) : - fromVersion > 0 ? false : IsValidWithLegacyRules(code)); + && (isCodeEof + ? EvmObjectFormat.IsValidEof(code, strategy, out _) + : (fromVersion > 0 ? false : IsValidWithLegacyRules(code))); return valid; } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index 44060fcdf56..d9a674c8221 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -28,14 +28,14 @@ public ReadOnlyMemory ContainerSection(int index) return Memory.Empty; else { - return MachineCode.Slice(Header.ContainerSection.Value.Start + offset.Value.Start, offset.Value.Size); + return MachineCode.Slice(Header.ContainerSections.Value.Start + offset.Value.Start, offset.Value.Size); } } public SectionHeader CodeSectionOffset(int sectionId) => Header.CodeSections[sectionId]; public SectionHeader? ContainerSectionOffset(int containerId) => - Header.ContainerSection is null + Header.ContainerSections is null ? null - : Header.ContainerSection.Value[containerId]; + : Header.ContainerSections.Value[containerId]; public (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) { ReadOnlySpan typesectionSpan = TypeSection.Span; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 5cf22b2ca62..ff808319908 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -106,8 +106,6 @@ public static bool IsEof(ReadOnlySpan container, [NotNullWhen(true)] out i } - public static bool IsEofn(ReadOnlySpan container, byte version) => container.Length >= MAGIC.Length + 1 && container.StartsWith(MAGIC) && container[MAGIC.Length] == version; - public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy strategy, [NotNullWhen(true)] out EofHeader? header) { if (strategy == ValidationStrategy.None) @@ -129,13 +127,13 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s bool validateBody = true || strategy.HasFlag(ValidationStrategy.Validate); if (validateBody && handler.ValidateBody(container, header.Value, strategy)) { - if (strategy.HasFlag(ValidationStrategy.ValidateSubContainers) && header?.ContainerSection?.Count > 0) + if (strategy.HasFlag(ValidationStrategy.ValidateSubContainers) && header?.ContainerSections?.Count > 0) { - int containerSize = header.Value.ContainerSection.Value.Count; + int containerSize = header.Value.ContainerSections.Value.Count; for (int i = 0; i < containerSize; i++) { - ReadOnlySpan subContainer = container.Slice(header.Value.ContainerSection.Value.Start + header.Value.ContainerSection.Value[i].Start, header.Value.ContainerSection.Value[i].Size); + ReadOnlySpan subContainer = container.Slice(header.Value.ContainerSections.Value.Start + header.Value.ContainerSections.Value[i].Start, header.Value.ContainerSections.Value[i].Size); if (!IsValidEof(subContainer, strategy, out _)) { return false; @@ -152,21 +150,6 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s return false; } - public static bool TryExtractHeader(ReadOnlySpan container, [NotNullWhen(true)] out EofHeader? header) - { - header = null; - return container.Length > VERSION_OFFSET - && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) - && handler.TryParseEofHeader(container, out header); - } - - public static byte GetCodeVersion(ReadOnlySpan container) - { - return container.Length <= VERSION_OFFSET - ? byte.MinValue - : container[VERSION_OFFSET]; - } - internal class Eof1 : IEofVersionHandler { private ref struct Sizes @@ -372,7 +355,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => PrefixSize = HEADER_TERMINATOR_OFFSET, TypeSection = typeSectionHeader, CodeSections = codeSectionHeader, - ContainerSection = containerSectionHeader, + ContainerSections = containerSectionHeader, DataSection = dataSectionHeader, }; @@ -386,17 +369,17 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat int calculatedCodeLength = header.TypeSection.Size + header.CodeSections.Size - + (header.ContainerSection?.Size ?? 0); + + (header.ContainerSections?.Size ?? 0); CompoundSectionHeader codeSections = header.CodeSections; ReadOnlySpan contractBody = container[startOffset..endOffset]; ReadOnlySpan dataBody = container[endOffset..]; var typeSection = header.TypeSection; (int typeSectionStart, int typeSectionSize) = (typeSection.Start, typeSection.Size); - if (header.ContainerSection?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) + if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) { // move this check where `header.ExtraContainers.Count` is parsed - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSection?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSections?.Count}"); return false; } @@ -437,8 +420,8 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat return false; } - bool[] visitedSectionsArray = ArrayPool.Shared.Rent(header.CodeSections.Count); - Span visitedSections = visitedSectionsArray.AsSpan(0, header.CodeSections.Count); + Span visitedSections = stackalloc bool[header.CodeSections.Count]; + Span visitedContainerSections = stackalloc byte[header.ContainerSections is null ? 0 : header.ContainerSections.Value.Count]; visitedSections.Clear(); Queue validationQueue = new Queue(); @@ -456,25 +439,17 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat var codeSection = header.CodeSections[sectionIdx]; ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSection.Start, codeSection.Size); - if (!ValidateInstructions(sectionIdx, strategy, typesection, code, header, container, validationQueue)) + if (!ValidateInstructions(sectionIdx, strategy, typesection, code, header, container, validationQueue, ref visitedContainerSections)) { - ArrayPool.Shared.Return(visitedSectionsArray); return false; } } - bool HasNoNonReachableCodeSections = false; - for (int i = 0; i < header.CodeSections.Count; i++) - { - if (!visitedSections[i]) - { - HasNoNonReachableCodeSections = true; - break; - } - } + bool HasNoNonReachableSections = + visitedSections[..header.CodeSections.Count].Contains(false) + || (header.ContainerSections is not null && visitedContainerSections[..header.ContainerSections.Value.Count].Contains((byte)0)); - ArrayPool.Shared.Return(visitedSectionsArray); - return !HasNoNonReachableCodeSections; + return !HasNoNonReachableSections; } bool ValidateTypeSection(ReadOnlySpan types) @@ -518,11 +493,12 @@ bool ValidateTypeSection(ReadOnlySpan types) return true; } - bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist) + bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist, ref Span visitedContainers) { int length = (code.Length / BYTE_BIT_COUNT) + 1; byte[] codeBitmapArray = ArrayPool.Shared.Rent(length); byte[] jumpDestsArray = ArrayPool.Shared.Rent(length); + try { // ArrayPool may return a larger array than requested, so we need to slice it to the actual length @@ -712,13 +688,21 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl } ushort runtimeContainerId = code[postInstructionByte]; - if (runtimeContainerId >= header.ContainerSection?.Count) + if (runtimeContainerId >= header.ContainerSections?.Count) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {header.ContainerSections?.Count}"); + return false; + } + + if (visitedContainers[runtimeContainerId] != 0 && visitedContainers[runtimeContainerId] != (byte)ValidationStrategy.ValidateRuntimeMode) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {header.ContainerSection?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); return false; } - ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value.Start + header.ContainerSection.Value[runtimeContainerId].Start, header.ContainerSection.Value[runtimeContainerId].Size); + visitedContainers[runtimeContainerId] = (byte)ValidationStrategy.ValidateRuntimeMode; + ReadOnlySpan subcontainer = container.Slice(header.ContainerSections.Value.Start + header.ContainerSections.Value[runtimeContainerId].Start, header.ContainerSections.Value[runtimeContainerId].Size); + if (!IsValidEof(subcontainer, ValidationStrategy.ValidateRuntimeMode, out _)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate must be a valid Eof"); @@ -739,13 +723,20 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl byte initcodeSectionId = code[postInstructionByte]; BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - if (initcodeSectionId >= header.ContainerSection?.Count) + if (initcodeSectionId >= header.ContainerSections?.Count) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {header.CodeSections.Count}"); return false; } - ReadOnlySpan subcontainer = container.Slice(header.ContainerSection.Value.Start + header.ContainerSection.Value[initcodeSectionId].Start, header.ContainerSection.Value[initcodeSectionId].Size); + if (visitedContainers[initcodeSectionId] != 0 && visitedContainers[initcodeSectionId] != (byte)ValidationStrategy.ValidateInitcodeMode) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s target container can only be a initcode mode bytecode"); + return false; + } + + visitedContainers[initcodeSectionId] = (byte)ValidationStrategy.ValidateInitcodeMode; + ReadOnlySpan subcontainer = container.Slice(header.ContainerSections.Value.Start + header.ContainerSections.Value[initcodeSectionId].Start, header.ContainerSections.Value[initcodeSectionId].Size); if (!IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody | ValidationStrategy.ValidateInitcodeMode, out _)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must be a valid Eof"); diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index ae21551bad5..9e60a485fd4 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -14,7 +14,7 @@ public struct EofHeader() public required int PrefixSize; public required SectionHeader TypeSection; public required CompoundSectionHeader CodeSections; - public required CompoundSectionHeader? ContainerSection; + public required CompoundSectionHeader? ContainerSections; public required SectionHeader DataSection; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d310ab1cb7f..71fdc8cdde2 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -90,6 +90,7 @@ internal readonly ref struct CallResult public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); public static CallResult InvalidAddressRange => new(EvmExceptionType.AddressOutOfRange); + public static CallResult InvalidDataSectionIndex => new(EvmExceptionType.DataSectionIndexOutOfRange); public static object BoxedEmpty { get; } = new object(); public static CallResult Empty(int fromVersion) => new(default, null, fromVersion); @@ -410,9 +411,9 @@ public TransactionSubstate Run(EvmState state, IWorldState worl EvmObjectFormat.VERSION_OFFSET // magic + version + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) - + (eofCodeInfo.Header.ContainerSection is null + + (eofCodeInfo.Header.ContainerSections is null ? 0 // container section : (0 bytes if no container section is available) - : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.ContainerSection.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator bytecodeResult[dataSubheaderSectionStart] = (byte)(bytecodeResult.Length >> 8); bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(bytecodeResult.Length & 0xFF); @@ -2308,12 +2309,12 @@ private CallResult ExecuteCode auxData = ReadOnlyMemory.Empty; if (b > UInt256.Zero) { + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; if(dataSection.Length + (int)b > (env.CodeInfo as EofCodeInfo).Header.DataSection.Size) { goto DataSectionAccessViolation; } - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; auxData = vmState.Memory.Load(a, b); } @@ -2936,7 +2937,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header ReadOnlyMemory initcontainer = container.ContainerSection(initcontainerIndex); - int initcontainerSize = container.Header.ContainerSection.Value[initcontainerIndex].Size; + int initcontainerSize = container.Header.ContainerSections.Value[initcontainerIndex].Size; // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) if (!UpdateGas(GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize), ref gasAvailable)) @@ -3410,6 +3411,7 @@ private CallResult GetFailureReturn(long gasAvailable, Evm EvmExceptionType.InvalidJumpDestination => CallResult.InvalidJumpDestination, EvmExceptionType.AccessViolation => CallResult.AccessViolationException, EvmExceptionType.AddressOutOfRange => CallResult.InvalidAddressRange, + EvmExceptionType.DataSectionIndexOutOfRange => CallResult.InvalidDataSectionIndex, _ => throw new ArgumentOutOfRangeException(nameof(exceptionType), exceptionType, "") }; } From bdbb912203c0739be01caa32b79004919e032cf7 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 4 Jul 2024 14:04:22 +0100 Subject: [PATCH 076/255] Formatting --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 71fdc8cdde2..2fba0f16115 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2310,7 +2310,7 @@ private CallResult ExecuteCode UInt256.Zero) { if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; - if(dataSection.Length + (int)b > (env.CodeInfo as EofCodeInfo).Header.DataSection.Size) + if (dataSection.Length + (int)b > (env.CodeInfo as EofCodeInfo).Header.DataSection.Size) { goto DataSectionAccessViolation; } @@ -2928,7 +2928,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // no stack checks becaue EOF guarantees no stack undeflows stack.PopUInt256(out UInt256 value); stack.PopWord256(out Span salt); - stack.PopUInt256(out UInt256 dataOffset) ; + stack.PopUInt256(out UInt256 dataOffset); stack.PopUInt256(out UInt256 dataSize); // 4 - perform (and charge for) memory expansion using [input_offset, input_size] @@ -2946,7 +2946,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // 7 - check that current call depth is below STACK_DEPTH_LIMIT and that caller balance is enough to transfer value // in case of failure return 0 on the stack, caller’s nonce is not updated and gas for initcode execution is not consumed. UInt256 balance = _state.GetBalance(env.ExecutingAccount); - if (env.CallDepth >= MaxCallDepth || value > balance) + if (env.CallDepth >= MaxCallDepth || value > balance) { // TODO: need a test for this _returnDataBuffer = Array.Empty(); From 151398fac20f8b58cd7b93543b7c5cd8bb8fac32 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 5 Jul 2024 01:43:28 +0100 Subject: [PATCH 077/255] - some EofCreate/ReturnContract fixes - new HeaderParsing Method (loop + swtich) --- src/Nethermind/Nethermind.Evm/EvmException.cs | 1 - .../EvmObjectFormat/EofCodeValidator.cs | 217 +++++++++++++++++- .../Nethermind.Evm/VirtualMachine.cs | 16 +- 3 files changed, 212 insertions(+), 22 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmException.cs b/src/Nethermind/Nethermind.Evm/EvmException.cs index 4f00dc5a3ca..2849a6dd796 100644 --- a/src/Nethermind/Nethermind.Evm/EvmException.cs +++ b/src/Nethermind/Nethermind.Evm/EvmException.cs @@ -30,6 +30,5 @@ public enum EvmExceptionType Other, Revert, InvalidCode, - DataSectionIndexOutOfRange } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index ff808319908..d78eccb6ef4 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -134,7 +134,7 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s for (int i = 0; i < containerSize; i++) { ReadOnlySpan subContainer = container.Slice(header.Value.ContainerSections.Value.Start + header.Value.ContainerSections.Value[i].Start, header.Value.ContainerSections.Value[i].Size); - if (!IsValidEof(subContainer, strategy, out _)) + if (!IsValidEof(subContainer, ValidationStrategy.Validate, out _)) { return false; } @@ -154,10 +154,10 @@ internal class Eof1 : IEofVersionHandler { private ref struct Sizes { - public ushort TypeSectionSize; - public ushort CodeSectionSize; - public ushort DataSectionSize; - public ushort ContainerSectionSize; + public ushort? TypeSectionSize; + public ushort? CodeSectionSize; + public ushort? DataSectionSize; + public ushort? ContainerSectionSize; } public const byte VERSION = 0x01; @@ -204,6 +204,7 @@ internal enum Separator : byte + MINIMUM_TYPESECTION_SIZE // minimum type section body size + MINIMUM_CODESECTION_SIZE // minimum code section body size + MINIMUM_DATASECTION_SIZE; // minimum data section body size + public bool TryParseEofHeader(ReadOnlySpan container, out EofHeader? header) { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -230,6 +231,206 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => return false; } + Sizes sectionSizes = new(); + int[] codeSections = null; + int[] containerSections = null; + int pos = VERSION_OFFSET + 1; + + bool continueParsing = true; + while(continueParsing && pos < container.Length) + { + Separator separator = (Separator)container[pos++]; + + switch(separator) + { + case Separator.KIND_TYPE: + 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; + } + + sectionSizes.TypeSectionSize = GetUInt16(container, pos); + if (sectionSizes.TypeSectionSize < MINIMUM_TYPESECTION_SIZE) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); + return false; + } + + pos += TWO_BYTE_LENGTH; + break; + case Separator.KIND_CODE: + if(sectionSizes.TypeSectionSize is null) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); + return false; + } + + 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(container, pos); + sectionSizes.CodeSectionSize = (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; + } + + if(container.Length < pos + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * numberOfCodeSections) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + codeSections = new int[numberOfCodeSections]; + int CODESECTION_HEADER_PREFIX_SIZE = pos + TWO_BYTE_LENGTH; + for (ushort i = 0; i < numberOfCodeSections; i++) + { + int currentCodeSizeOffset = CODESECTION_HEADER_PREFIX_SIZE + i * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size + int codeSectionSize = GetUInt16(container, currentCodeSizeOffset); + + 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 + TWO_BYTE_LENGTH * numberOfCodeSections; + break; + case Separator.KIND_CONTAINER: + if(sectionSizes.CodeSectionSize is null) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); + return false; + } + + 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(container, pos); + sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * TWO_BYTE_LENGTH); + if (numberOfContainerSections is > MAXIMUM_NUM_CONTAINER_SECTIONS or 0) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); + return false; + } + + if (container.Length < pos + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * numberOfContainerSections) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + containerSections = new int[numberOfContainerSections]; + int CONTAINER_SECTION_HEADER_PREFIX_SIZE = pos + TWO_BYTE_LENGTH; + for (ushort i = 0; i < numberOfContainerSections; i++) + { + int currentContainerSizeOffset = CONTAINER_SECTION_HEADER_PREFIX_SIZE + i * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size + int containerSectionSize = GetUInt16(container, currentContainerSizeOffset); + + 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 + TWO_BYTE_LENGTH * numberOfContainerSections; + break; + case Separator.KIND_DATA: + if (sectionSizes.CodeSectionSize is null) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); + return false; + } + + 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; + } + + sectionSizes.DataSectionSize = GetUInt16(container, pos); + + pos += TWO_BYTE_LENGTH; + break; + case Separator.TERMINATOR: + 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; + } + } + + 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; + } + + SectionHeader typeSectionSubHeader = new SectionHeader(pos, sectionSizes.TypeSectionSize.Value); + CompoundSectionHeader codeSectionSubHeader = new CompoundSectionHeader(typeSectionSubHeader.EndOffset, codeSections); + CompoundSectionHeader? containerSectionSubHeader = containerSections is null ? null + : new CompoundSectionHeader(codeSectionSubHeader.EndOffset, containerSections); + SectionHeader dataSectionSubHeader = new SectionHeader(containerSectionSubHeader?.EndOffset ?? codeSectionSubHeader.EndOffset, sectionSizes.DataSectionSize.Value); + + header = new EofHeader + { + Version = VERSION, + PrefixSize = pos, + TypeSection = typeSectionSubHeader, + CodeSections = codeSectionSubHeader, + ContainerSections = containerSectionSubHeader, + DataSection = dataSectionSubHeader, + }; + + return true; + } + public bool TryParseEofHeader2(ReadOnlySpan container, out EofHeader? header) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ushort GetUInt16(ReadOnlySpan container, int offset) => + container.Slice(offset, TWO_BYTE_LENGTH).ReadEthUInt16(); + + header = null; + // we need to be able to parse header + minimum section lenghts + 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.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; + } + Sizes sectionSizes = new(); int TYPESECTION_HEADER_STARTOFFSET = VERSION_OFFSET + ONE_BYTE_LENGTH; @@ -330,7 +531,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => SectionHeader typeSectionHeader = new ( start: HEADER_TERMINATOR_OFFSET + ONE_BYTE_LENGTH, - size: sectionSizes.TypeSectionSize + size: sectionSizes.TypeSectionSize.Value ); CompoundSectionHeader codeSectionHeader = new( @@ -346,7 +547,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => SectionHeader dataSectionHeader = new( start: containerSectionHeader?.EndOffset ?? codeSectionHeader.EndOffset, - size: sectionSizes.DataSectionSize + size: sectionSizes.DataSectionSize.Value ); header = new EofHeader @@ -737,7 +938,7 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl visitedContainers[initcodeSectionId] = (byte)ValidationStrategy.ValidateInitcodeMode; ReadOnlySpan subcontainer = container.Slice(header.ContainerSections.Value.Start + header.ContainerSections.Value[initcodeSectionId].Start, header.ContainerSections.Value[initcodeSectionId].Size); - if (!IsValidEof(subcontainer, ValidationStrategy.ValidateFullBody | ValidationStrategy.ValidateInitcodeMode, out _)) + if (!IsValidEof(subcontainer, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateSubContainers | ValidationStrategy.ValidateFullBody, out _)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must be a valid Eof"); return false; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 2fba0f16115..f6a454f077d 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -90,7 +90,6 @@ internal readonly ref struct CallResult public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); public static CallResult InvalidAddressRange => new(EvmExceptionType.AddressOutOfRange); - public static CallResult InvalidDataSectionIndex => new(EvmExceptionType.DataSectionIndexOutOfRange); public static object BoxedEmpty { get; } = new object(); public static CallResult Empty(int fromVersion) => new(default, null, fromVersion); @@ -415,15 +414,15 @@ public TransactionSubstate Run(EvmState state, IWorldState worl ? 0 // container section : (0 bytes if no container section is available) : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator + + ushort dataSize = (ushort)(eofCodeInfo.Header.DataSection.Size + auxExtraData.Length); bytecodeResult[dataSubheaderSectionStart] = (byte)(bytecodeResult.Length >> 8); bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(bytecodeResult.Length & 0xFF); bytecodeResultArray = bytecodeResult.ToArray(); - int dataSize = bytecodeResult[dataSubheaderSectionStart..].ReadEthUInt16(); - // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); + bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize) || dataSize != eofCodeInfo.Header.DataSection.Size; long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray?.Length ?? 0, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { @@ -2305,15 +2304,10 @@ private CallResult ExecuteCode auxData = ReadOnlyMemory.Empty; if (b > UInt256.Zero) { if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; - if (dataSection.Length + (int)b > (env.CodeInfo as EofCodeInfo).Header.DataSection.Size) - { - goto DataSectionAccessViolation; - } auxData = vmState.Memory.Load(a, b); } @@ -2441,9 +2435,6 @@ private CallResult ExecuteCode(gasAvailable, exceptionType); InvalidSubroutineEntry: @@ -3411,7 +3402,6 @@ private CallResult GetFailureReturn(long gasAvailable, Evm EvmExceptionType.InvalidJumpDestination => CallResult.InvalidJumpDestination, EvmExceptionType.AccessViolation => CallResult.AccessViolationException, EvmExceptionType.AddressOutOfRange => CallResult.InvalidAddressRange, - EvmExceptionType.DataSectionIndexOutOfRange => CallResult.InvalidDataSectionIndex, _ => throw new ArgumentOutOfRangeException(nameof(exceptionType), exceptionType, "") }; } From e684eff054bc925e6b19ba3ba26a9ea71e61ce10 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 8 Jul 2024 02:03:22 +0100 Subject: [PATCH 078/255] some refactors to CodeInfo and EofCodeInfo --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 9 +++------ src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs | 13 ++++++------- src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs | 6 +++--- .../Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs | 12 ++---------- .../Nethermind.Evm/EvmObjectFormat/EofHeader.cs | 6 ++++++ src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 8 ++++---- 7 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index f7aa87037c5..509118577d0 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; using Nethermind.Evm.EOF; using Nethermind.Evm.Precompiles; +using System.Diagnostics; namespace Nethermind.Evm.CodeAnalysis { @@ -59,13 +60,9 @@ public void AnalyseInBackgroundIfRequired() } public SectionHeader CodeSectionOffset(int idx) - { - throw new NotImplementedException(); - } + => throw new UnreachableException(); public SectionHeader? ContainerSectionOffset(int idx) - { - throw new NotImplementedException(); - } + => throw new UnreachableException(); } } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index def1b057892..f60219e498d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -9,20 +9,18 @@ namespace Nethermind.Evm.CodeAnalysis; public static class CodeInfoFactory { - public static bool CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy validationRules = EvmObjectFormat.ValidationStrategy.ExractHeader) + public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, EvmObjectFormat.ValidationStrategy validationRules = EvmObjectFormat.ValidationStrategy.ExractHeader) { - codeinfo = new CodeInfo(code); + CodeInfo codeInfo = new CodeInfo(code); if (spec.IsEofEnabled && code.Span.StartsWith(EvmObjectFormat.MAGIC)) { if (EvmObjectFormat.IsValidEof(code.Span, validationRules, out EofHeader? header)) { - codeinfo = new EofCodeInfo(codeinfo, header.Value); - return true; + return new EofCodeInfo(codeInfo, header.Value); } - return false; } - (codeinfo as CodeInfo).AnalyseInBackgroundIfRequired(); - return true; + codeInfo.AnalyseInBackgroundIfRequired(); + return codeInfo; } public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out ICodeInfo codeinfo, out Memory extraCalldata) @@ -39,6 +37,7 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out } return false; } + (codeinfo as CodeInfo).AnalyseInBackgroundIfRequired(); return true; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index f33770738f5..49a508b52fa 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -126,7 +126,7 @@ public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, I MissingCode(codeSource, codeHash); } - CodeInfoFactory.CreateCodeInfo(code, vmSpec, out cachedCodeInfo, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); + cachedCodeInfo = CodeInfoFactory.CreateCodeInfo(code, vmSpec, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); _codeCache.Set(codeHash, cachedCodeInfo); } else @@ -148,7 +148,7 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR { if (!_codeCache.TryGet(codeHash, out ICodeInfo? codeInfo)) { - CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, out codeInfo, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); + codeInfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); // Prime the code cache as likely to be used by more txs _codeCache.Set(codeHash, codeInfo); @@ -160,7 +160,7 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - CodeInfoFactory.CreateCodeInfo(code, spec, out ICodeInfo codeInfo, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); + ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); Hash256 codeHash = code.Length == 0 ? Keccak.OfAnEmptyString : Keccak.Compute(code.Span); state.InsertCode(codeOwner, codeHash, code, spec); _codeCache.Set(codeHash, codeInfo); diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index d9a674c8221..0bbc905e669 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -20,17 +20,8 @@ public class EofCodeInfo : ICodeInfo public ReadOnlyMemory TypeSection { get; } public ReadOnlyMemory CodeSection { get; } public ReadOnlyMemory DataSection { get; } + public ReadOnlyMemory ContainerSection { get; } - public ReadOnlyMemory ContainerSection(int index) - { - var offset = ContainerSectionOffset(index); - if (offset is null) - return Memory.Empty; - else - { - return MachineCode.Slice(Header.ContainerSections.Value.Start + offset.Value.Start, offset.Value.Size); - } - } public SectionHeader CodeSectionOffset(int sectionId) => Header.CodeSections[sectionId]; public SectionHeader? ContainerSectionOffset(int containerId) => Header.ContainerSections is null @@ -55,5 +46,6 @@ public EofCodeInfo(ICodeInfo codeInfo, in EofHeader header) TypeSection = MachineCode.Slice(Header.TypeSection.Start, Header.TypeSection.Size); CodeSection = MachineCode.Slice(Header.CodeSections.Start, Header.CodeSections.Size); DataSection = MachineCode.Slice(Header.DataSection.Start); + ContainerSection = Header.ContainerSections is null ? Memory.Empty : MachineCode.Slice(Header.ContainerSections.Value.Start, Header.ContainerSections.Value.Size); } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index 9e60a485fd4..b2c2c4f885c 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Drawing; using System.Linq; using Nethermind.Core.Extensions; @@ -23,6 +24,8 @@ public 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 struct CompoundSectionHeader(int start, int[] subSectionsSizes) @@ -55,4 +58,7 @@ private int[] SubSectionsSizesAcc } public SectionHeader this[int i] => new SectionHeader(SubSectionsSizesAcc[i], (ushort)SubSectionsSizes[i]); + + public static implicit operator Range(CompoundSectionHeader section) => new(section.Start, section.EndOffset); } + diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index 8a9a245eb42..db6f61fbb63 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -17,7 +17,7 @@ public interface ICodeInfo ReadOnlyMemory TypeSection => Memory.Empty; ReadOnlyMemory CodeSection => MachineCode; ReadOnlyMemory DataSection => Memory.Empty; - ReadOnlyMemory ContainerSection(int _) => Memory.Empty; + ReadOnlyMemory ContainerSection => Memory.Empty; SectionHeader CodeSectionOffset(int idx); SectionHeader? ContainerSectionOffset(int idx); (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f6a454f077d..1088b458be3 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -395,7 +395,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed int deployContainerIndex = callResult.Output.ContainerIndex.Value; ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; - ReadOnlySpan container = eofCodeInfo.ContainerSection(deployContainerIndex).Span; + ReadOnlySpan container = eofCodeInfo.ContainerSection.Span[(Range)eofCodeInfo.ContainerSectionOffset(deployContainerIndex).Value]; byte[] bytecodeResultArray = null; // 2 - concatenate data section with (aux_data_offset, aux_data_offset + aux_data_size) memory segment and update data size in the header @@ -2927,7 +2927,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header - ReadOnlyMemory initcontainer = container.ContainerSection(initcontainerIndex); + ReadOnlySpan initcontainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initcontainerIndex).Value]; int initcontainerSize = container.Header.ContainerSections.Value[initcontainerIndex].Size; // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) @@ -2963,7 +2963,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref } // 11 - calculate new_address as keccak256(0xff || sender || salt || keccak256(initcontainer))[12:] - Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initcontainer.Span); + Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initcontainer); if (spec.UseHotAndColdStorage) { // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 @@ -2995,7 +2995,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref _state.IncrementNonce(env.ExecutingAccount); - CodeInfoFactory.CreateCodeInfo(initcontainer.ToArray(), spec, out ICodeInfo codeinfo, EvmObjectFormat.ValidationStrategy.ExractHeader); + ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initcontainer.ToArray(), spec, EvmObjectFormat.ValidationStrategy.ExractHeader); ExecutionEnvironment callEnv = new ( From 103f834ce2ec8b92b0d8b73df2d099b18dd915c6 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 9 Jul 2024 13:43:58 +0100 Subject: [PATCH 079/255] formatting --- .../EvmObjectFormat/EofCodeValidator.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index d78eccb6ef4..16c34861baf 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -237,16 +237,16 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => int pos = VERSION_OFFSET + 1; bool continueParsing = true; - while(continueParsing && pos < container.Length) + while (continueParsing && pos < container.Length) { Separator separator = (Separator)container[pos++]; - switch(separator) + switch (separator) { case Separator.KIND_TYPE: - if(container.Length < pos + TWO_BYTE_LENGTH) + if (container.Length < pos + TWO_BYTE_LENGTH) { - if(Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); return false; } @@ -260,7 +260,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => pos += TWO_BYTE_LENGTH; break; case Separator.KIND_CODE: - if(sectionSizes.TypeSectionSize is null) + if (sectionSizes.TypeSectionSize is null) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); return false; @@ -280,7 +280,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => return false; } - if(container.Length < pos + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * numberOfCodeSections) + if (container.Length < pos + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * numberOfCodeSections) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); return false; @@ -305,7 +305,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => pos += TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * numberOfCodeSections; break; case Separator.KIND_CONTAINER: - if(sectionSizes.CodeSectionSize is null) + if (sectionSizes.CodeSectionSize is null) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); return false; From e4b2ca6d688b0b370289957c802a985da41d3372 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 9 Jul 2024 14:44:06 +0100 Subject: [PATCH 080/255] Increment Nonce before snapshot --- src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs | 2 +- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 1 + .../TransactionProcessing/TransactionProcessor.cs | 3 +-- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 12 ++++-------- .../Nethermind.Facade/Eth/TransactionForRpc.cs | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index f60219e498d..5244df1fde2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -37,7 +37,7 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out } return false; } - (codeinfo as CodeInfo).AnalyseInBackgroundIfRequired(); + codeinfo.AnalyseInBackgroundIfRequired(); return true; } } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index db6f61fbb63..36b7ff0db10 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -21,4 +21,5 @@ public interface ICodeInfo SectionHeader CodeSectionOffset(int idx); SectionHeader? ContainerSectionOffset(int idx); (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); + void AnalyseInBackgroundIfRequired() { } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index a41a432107c..4edc9ea67d1 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -435,8 +435,7 @@ protected TransactionResult BuildExecutionEnvironment( else { codeInfo = _codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec); - if (codeInfo is CodeInfo eofv0CodeInfo) - eofv0CodeInfo.AnalyseInBackgroundIfRequired(); + codeInfo.AnalyseInBackgroundIfRequired(); } env = new ExecutionEnvironment diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 1088b458be3..49ea36921d7 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2538,8 +2538,7 @@ private EvmExceptionType InstructionCall( !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; ICodeInfo codeInfo = _codeInfoRepository.GetCachedCodeInfo(_state, codeSource, spec); - if (codeInfo is CodeInfo eof0CodeInfo) - eof0CodeInfo.AnalyseInBackgroundIfRequired(); + codeInfo.AnalyseInBackgroundIfRequired(); if (spec.Use63Over64Rule) { @@ -3108,6 +3107,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref vmState.WarmUp(contractAddress); } + _state.IncrementNonce(env.ExecutingAccount); + // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid // for another tx as returned to pool. @@ -3120,10 +3121,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref } CodeInfoFactory.CreateInitCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, out _); - if (codeinfo is CodeInfo classicalCode) - { - classicalCode.AnalyseInBackgroundIfRequired(); - } + codeinfo.AnalyseInBackgroundIfRequired(); Snapshot snapshot = _state.TakeSnapshot(); @@ -3145,8 +3143,6 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - _state.IncrementNonce(env.ExecutingAccount); - ExecutionEnvironment callEnv = new ( txExecutionContext: in env.TxExecutionContext, diff --git a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs index 2b1792db654..d51ed363156 100644 --- a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs +++ b/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs @@ -159,7 +159,7 @@ public TransactionForRpc() { } AccessList = TryGetAccessList(), ChainId = chainId, DecodedMaxFeePerGas = MaxFeePerGas ?? 0, - Hash = Hash, + Hash = Hash }; if (tx.Supports1559) From c6ff84fa3393bab1139acbf8593d0d9d8ab5eccb Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 9 Jul 2024 15:14:13 +0100 Subject: [PATCH 081/255] Comment out new tests --- .../PragueStateTests.cs | 32 +++++++++---------- .../Ethereum.Blockchain.Test/EofTests.cs | 10 +++--- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index dd45528a7a8..ec74ad153fb 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -9,20 +9,20 @@ namespace Ethereum.Blockchain.Pyspec.Test; -[TestFixture] -[Parallelizable(ParallelScope.All)] -public class PragueStateTests : GeneralStateTestBase -{ - [TestCaseSource(nameof(LoadTests))] - public void Test(GeneralStateTest test) => RunTest(test).Pass.Should().BeTrue(); +//[TestFixture] +//[Parallelizable(ParallelScope.All)] +//public class PragueStateTests : GeneralStateTestBase +//{ +// [TestCaseSource(nameof(LoadTests))] +// public void Test(GeneralStateTest test) => RunTest(test).Pass.Should().BeTrue(); - private static IEnumerable LoadTests() - { - TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() - { - ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.4" - }, $"fixtures/state_tests/prague"); - return loader.LoadTests().Cast(); - } -} +// private static IEnumerable LoadTests() +// { +// TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() +// { +// ArchiveName = "fixtures_eip7692.tar.gz", +// ArchiveVersion = "eip7692@v1.0.4" +// }, $"fixtures/state_tests/prague"); +// return loader.LoadTests().Cast(); +// } +//} diff --git a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs index 10f09551575..e9c7de9a187 100644 --- a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs @@ -14,11 +14,11 @@ public class EOFTests : GeneralStateTestBase { // Uncomment when EOF tests are merged - [TestCaseSource(nameof(LoadTests))] - public void Test(GeneralStateTest test) - { - Assert.True(RunTest(test).Pass); - } + //[TestCaseSource(nameof(LoadTests))] + //public void Test(GeneralStateTest test) + //{ + // Assert.True(RunTest(test).Pass); + //} public static IEnumerable LoadTests() { From 1864710be947b3940e766cb4a929faf1e713fa29 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 9 Jul 2024 15:21:34 +0100 Subject: [PATCH 082/255] Revert "Comment out new tests" This reverts commit c6ff84fa3393bab1139acbf8593d0d9d8ab5eccb. --- .../PragueStateTests.cs | 32 +++++++++---------- .../Ethereum.Blockchain.Test/EofTests.cs | 10 +++--- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index ec74ad153fb..dd45528a7a8 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -9,20 +9,20 @@ namespace Ethereum.Blockchain.Pyspec.Test; -//[TestFixture] -//[Parallelizable(ParallelScope.All)] -//public class PragueStateTests : GeneralStateTestBase -//{ -// [TestCaseSource(nameof(LoadTests))] -// public void Test(GeneralStateTest test) => RunTest(test).Pass.Should().BeTrue(); +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class PragueStateTests : GeneralStateTestBase +{ + [TestCaseSource(nameof(LoadTests))] + public void Test(GeneralStateTest test) => RunTest(test).Pass.Should().BeTrue(); -// private static IEnumerable LoadTests() -// { -// TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() -// { -// ArchiveName = "fixtures_eip7692.tar.gz", -// ArchiveVersion = "eip7692@v1.0.4" -// }, $"fixtures/state_tests/prague"); -// return loader.LoadTests().Cast(); -// } -//} + private static IEnumerable LoadTests() + { + TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() + { + ArchiveName = "fixtures_eip7692.tar.gz", + ArchiveVersion = "eip7692@v1.0.4" + }, $"fixtures/state_tests/prague"); + return loader.LoadTests().Cast(); + } +} diff --git a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs index e9c7de9a187..10f09551575 100644 --- a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs @@ -14,11 +14,11 @@ public class EOFTests : GeneralStateTestBase { // Uncomment when EOF tests are merged - //[TestCaseSource(nameof(LoadTests))] - //public void Test(GeneralStateTest test) - //{ - // Assert.True(RunTest(test).Pass); - //} + [TestCaseSource(nameof(LoadTests))] + public void Test(GeneralStateTest test) + { + Assert.True(RunTest(test).Pass); + } public static IEnumerable LoadTests() { From 051c7862e90102e5ba01e47b3ac62dce92f68e74 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 9 Jul 2024 22:36:00 +0100 Subject: [PATCH 083/255] fix exchange failing tests in pyspec --- .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 22 +++++++++++++------ .../EvmObjectFormat/EofCodeValidator.cs | 14 ++++++------ src/Nethermind/Nethermind.Evm/EvmStack.cs | 15 +++++++------ .../Nethermind.Evm/VirtualMachine.cs | 9 ++++---- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 91482d7d713..400b88059fe 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using FluentAssertions; +using Nethermind.Core.Collections; using Nethermind.Core.Specs; using Nethermind.Core.Test; using Nethermind.Logging; @@ -197,14 +198,25 @@ public void Test(long blockNumber, ulong? timestamp = null) .Op((byte)i) .Done; + // hexString to array + if (InstructionExtensions.IsValid(opcode, IsEofContext: true) && !InstructionExtensions.IsValid(opcode, IsEofContext: false)) { + // will be tested in EOFCREATE container validations + if (opcode is Instruction.RETURNCONTRACT) continue; + var opcodeMetadata = InstructionExtensions.StackRequirements(opcode); opcodeMetadata.InputCount ??= 1; opcodeMetadata.OutputCount ??= (opcode is Instruction.DUPN ? (ushort)2 : (ushort)1); bool isFunCall = opcode is Instruction.CALLF; + bool isCreateCall = opcode is Instruction.EOFCREATE; + byte[] runtimeContainer = Nethermind.Core.Extensions.Bytes.FromHexString("EF00010100040200010001040000000080000000"); + byte[] initcodeContainer = Nethermind.Core.Extensions.Bytes.FromHexString("EF00010100040200010004030001001404000000008000025F5FEE00") + .Concat(runtimeContainer) + .ToArray(); + byte[] stackHeighExpected = BitConverter.GetBytes(Math.Max(opcodeMetadata.InputCount.Value, opcodeMetadata.OutputCount.Value)); List codesection = new(); @@ -246,6 +258,7 @@ public void Test(long blockNumber, ulong? timestamp = null) byte[] codeSectionSize = BitConverter.GetBytes((ushort)(codesection.Count)); + byte[] containerSectionSize = BitConverter.GetBytes((ushort)(initcodeContainer.Length)); code = [ // start header 0xef, @@ -260,11 +273,7 @@ public void Test(long blockNumber, ulong? timestamp = null) codeSectionSize[1], codeSectionSize[0], .. (isFunCall ? [0x00, 0x01] : Array.Empty()), - 0x03, - 0x00, - 0x01, - 0x00, - 0x02, + .. (isCreateCall ? [0x03, 0x00, 0x01, containerSectionSize[1], containerSectionSize[0]] : Array.Empty()), 0x04, 0x00, 0x20, @@ -286,8 +295,7 @@ public void Test(long blockNumber, ulong? timestamp = null) // end codesection 1 // end codesection // start container section - (byte)Instruction.RETURNCONTRACT, - 0x00, + .. (isCreateCall ? initcodeContainer : Array.Empty()), // end container section // start data section .. Enumerable.Range(0, 32).Select(b => (byte)b).ToArray() diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 16c34861baf..6188e7d83e3 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -1040,18 +1040,18 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } break; case Instruction.DUPN: - byte imm = code[posPostInstruction]; - inputs = (ushort)(imm + 1); + int imm_n = 1 + code[posPostInstruction]; + inputs = (ushort)(imm_n); outputs = (ushort)(inputs + 1); break; case Instruction.SWAPN: - imm = code[posPostInstruction]; - outputs = inputs = (ushort)(2 + imm); + imm_n = 1 + code[posPostInstruction]; + outputs = inputs = (ushort)(1 + imm_n); break; case Instruction.EXCHANGE: - byte imm_n = (byte)(code[posPostInstruction] >> 4); - byte imm_m = (byte)(code[posPostInstruction] & 0x0F); - outputs = inputs = (ushort)(imm_n + imm_m + 3); + imm_n = 1 + (byte)(code[posPostInstruction] >> 4); + int imm_m = 1 + (byte)(code[posPostInstruction] & 0x0F); + outputs = inputs = (ushort)(imm_n + imm_m + 1); break; } diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 0050881b05a..2a53cf7271c 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -453,20 +453,21 @@ public readonly bool Swap(int depth) public readonly bool Exchange(int n, int m) { - if (!EnsureDepth(n + m + 1)) return false; + int maxDepth = Math.Max(n, m); + if (!EnsureDepth(maxDepth)) return false; ref byte bytes = ref MemoryMarshal.GetReference(_bytes); - ref byte bottom = ref Unsafe.Add(ref bytes, (Head - n - m - 1) * WordSize); - ref byte top = ref Unsafe.Add(ref bytes, (Head - n - 2) * WordSize); + ref byte first = ref Unsafe.Add(ref bytes, (Head - n) * WordSize); + ref byte second = ref Unsafe.Add(ref bytes, (Head - m) * WordSize); - Word buffer = Unsafe.ReadUnaligned(ref bottom); - Unsafe.WriteUnaligned(ref bottom, Unsafe.ReadUnaligned(ref top)); - Unsafe.WriteUnaligned(ref top, buffer); + Word buffer = Unsafe.ReadUnaligned(ref first); + Unsafe.WriteUnaligned(ref first, Unsafe.ReadUnaligned(ref second)); + Unsafe.WriteUnaligned(ref second, buffer); if (typeof(TTracing) == typeof(IsTracing)) { - Trace(n + m + 1); + Trace(maxDepth); } return true; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 49ea36921d7..d6d97844d67 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1868,8 +1868,8 @@ private CallResult ExecuteCode> 0x04); int m = 1 + (int)(codeSection[programCounter] & 0x0f); - stack.Exchange(n, m); + stack.Exchange(n + 1, m + n + 1); programCounter += 1; break; @@ -2969,6 +2969,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref vmState.WarmUp(contractAddress); } + _state.IncrementNonce(env.ExecutingAccount); + if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState?.Memory.Size ?? 0); // todo: === below is a new call - refactor / move @@ -2992,7 +2994,6 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - _state.IncrementNonce(env.ExecutingAccount); ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initcontainer.ToArray(), spec, EvmObjectFormat.ValidationStrategy.ExractHeader); From 5ad754a3e76ec970f87d48aafa01340ca3f3bf1f Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 10 Jul 2024 02:42:06 +0100 Subject: [PATCH 084/255] * fix Create2 in Eof * fix callF stack overflow check * fix some Ext*call callvalue handling --- src/Nethermind/Nethermind.Evm/EvmStack.cs | 2 +- .../Nethermind.Evm/VirtualMachine.cs | 36 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 2a53cf7271c..82bb5934fd7 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -492,7 +492,7 @@ public static class EvmStack { public const int RegisterLength = 1; public const int MaxStackSize = 1025; - public const int ReturnStackSize = 1023; + public const int ReturnStackSize = 1025; public const int WordSize = 32; public const int AddressSize = 20; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d6d97844d67..d51b9a5fdb8 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -352,7 +352,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (previousState.ExecutionType.IsAnyCreateLegacy()) { long codeDepositGasCost = CodeDepositHandler.CalculateCost(callResult.Output.Bytes.Length, spec); - bool invalidCode = !CodeDepositHandler.CodeIsValid(spec, callResult.Output.Bytes, callResult.FromVersion); + bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(callResult.Output.Bytes.Span); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { ReadOnlyMemory code = callResult.Output.Bytes; @@ -415,14 +415,14 @@ public TransactionSubstate Run(EvmState state, IWorldState worl : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator - ushort dataSize = (ushort)(eofCodeInfo.Header.DataSection.Size + auxExtraData.Length); + ushort dataSize = (ushort)(eofCodeInfo.DataSection.Length + auxExtraData.Length); bytecodeResult[dataSubheaderSectionStart] = (byte)(bytecodeResult.Length >> 8); bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(bytecodeResult.Length & 0xFF); bytecodeResultArray = bytecodeResult.ToArray(); // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize) || dataSize != eofCodeInfo.Header.DataSection.Size; + bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray?.Length ?? 0, spec); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { @@ -2224,7 +2224,7 @@ private CallResult ExecuteCode targetBytes); stack.PopUInt256(out UInt256 dataOffset); @@ -2692,12 +2698,11 @@ private EvmExceptionType InstructionEofCall= MaxCallDepth) { returnData = CallResult.BoxedEmpty; @@ -2758,7 +2763,6 @@ private EvmExceptionType InstructionEofCall callData = vmState.Memory.Load(in dataOffset, dataLength); Snapshot snapshot = _state.TakeSnapshot(); - if (!transferValue.IsZero) _state.SubtractFromBalance(caller, transferValue, spec); + if (!callValue.IsZero) _state.SubtractFromBalance(caller, callValue, spec); ExecutionEnvironment callEnv = new ( @@ -2792,7 +2796,7 @@ private EvmExceptionType InstructionEofCall(EvmState vmState, ref stack.PushZero(); return (EvmExceptionType.None, null); } + _state.IncrementNonce(env.ExecutingAccount); // 11 - calculate new_address as keccak256(0xff || sender || salt || keccak256(initcontainer))[12:] Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initcontainer); @@ -2969,7 +2974,6 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref vmState.WarmUp(contractAddress); } - _state.IncrementNonce(env.ExecutingAccount); if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState?.Memory.Size ?? 0); // todo: === below is a new call - refactor / move @@ -3108,8 +3112,6 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref vmState.WarmUp(contractAddress); } - _state.IncrementNonce(env.ExecutingAccount); - // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid // for another tx as returned to pool. @@ -3121,6 +3123,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.None, null); } + _state.IncrementNonce(env.ExecutingAccount); + CodeInfoFactory.CreateInitCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, out _); codeinfo.AnalyseInBackgroundIfRequired(); From d97d653815be8c4f7366eef430a621ee4218c4bb Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 10 Jul 2024 03:03:52 +0100 Subject: [PATCH 085/255] Fomratting --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d51b9a5fdb8..6db126d3c0b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2309,7 +2309,7 @@ private CallResult ExecuteCode Date: Wed, 10 Jul 2024 05:09:25 +0100 Subject: [PATCH 086/255] Fix All_tx_should_pass_before_3541 --- .../Nethermind.Evm/CodeDepositHandler.cs | 29 +++++-------------- .../TransactionProcessor.cs | 2 +- .../Nethermind.Evm/VirtualMachine.cs | 8 ++--- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs index cf7d1ea5adb..5c624d07b20 100644 --- a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs @@ -10,7 +10,7 @@ namespace Nethermind.Evm public static class CodeDepositHandler { private const byte InvalidStartingCodeByte = 0xEF; - public static long CalculateCost(int byteCodeLength, IReleaseSpec spec) + public static long CalculateCost(IReleaseSpec spec, int byteCodeLength) { if (spec.LimitCodeSize && byteCodeLength > spec.MaxCodeSize) return long.MaxValue; @@ -18,38 +18,23 @@ public static long CalculateCost(int byteCodeLength, IReleaseSpec spec) return GasCostOf.CodeDeposit * byteCodeLength; } - public static bool CodeIsInvalid(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion) => !CodeIsValid(spec, code, fromVersion); - public static bool CodeIsValid(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion) - { - bool valid = true; - if (spec.IsEofEnabled) - { - //fromVersion = (execType is ExecutionType.Create1 or ExecutionType.Create2) ? fromVersion : 0; //// hmmmm - valid = IsValidWithEofRules(code.Span, fromVersion); - } - else if (spec.IsEip3541Enabled) - { - valid = IsValidWithLegacyRules(code.Span); - } - return valid; - } + public static bool CodeIsValid(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion) + => spec.IsEofEnabled ? IsValidWithEofRules(spec, code.Span, fromVersion) : IsValidWithLegacyRules(spec, code.Span); - public static bool IsValidWithLegacyRules(ReadOnlySpan code) - { - return code is not [InvalidStartingCodeByte, ..]; ; - } + public static bool IsValidWithLegacyRules(IReleaseSpec spec, ReadOnlySpan code) + => !spec.IsEip3541Enabled || code is not [InvalidStartingCodeByte, ..]; - public static bool IsValidWithEofRules(ReadOnlySpan code, int fromVersion, EvmObjectFormat.ValidationStrategy strategy = EvmObjectFormat.ValidationStrategy.Validate) + public static bool IsValidWithEofRules(IReleaseSpec spec, ReadOnlySpan code, int fromVersion, EvmObjectFormat.ValidationStrategy strategy = EvmObjectFormat.ValidationStrategy.Validate) { bool isCodeEof = EvmObjectFormat.IsEof(code, out int codeVersion); bool valid = code.Length >= 1 && codeVersion >= fromVersion && (isCodeEof ? EvmObjectFormat.IsValidEof(code, strategy, out _) - : (fromVersion > 0 ? false : IsValidWithLegacyRules(code))); + : (fromVersion > 0 ? false : IsValidWithLegacyRules(spec, code))); return valid; } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 4edc9ea67d1..0bd1e3d64f3 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -531,7 +531,7 @@ protected void ExecuteEvmCall( // this may lead to inconsistencies (however it is tested extensively in blockchain tests) if (tx.IsContractCreation) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(substate.Output.Length, spec); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Length); if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) { ThrowOutOfGasException(); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 6db126d3c0b..60dc3643ab6 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -278,7 +278,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl { if (typeof(TTracingActions) == typeof(IsTracing)) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(callResult.Output.Bytes.Length, spec); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); if (callResult.IsException) { @@ -351,8 +351,8 @@ public TransactionSubstate Run(EvmState state, IWorldState worl previousCallOutput = ZeroPaddedSpan.Empty; if (previousState.ExecutionType.IsAnyCreateLegacy()) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(callResult.Output.Bytes.Length, spec); - bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(callResult.Output.Bytes.Span); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); + bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(spec, callResult.Output.Bytes.Span); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { ReadOnlyMemory code = callResult.Output.Bytes; @@ -423,7 +423,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); - long codeDepositGasCost = CodeDepositHandler.CalculateCost(bytecodeResultArray?.Length ?? 0, spec); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, bytecodeResultArray?.Length ?? 0); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { // 4 - set state[new_address].code to the updated deploy container From e676f6284c2e8cefd2767bcf2440844a0bb3b72f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 10 Jul 2024 06:07:46 +0100 Subject: [PATCH 087/255] Output differences --- src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs index e583a78b423..99f7f874ba7 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs @@ -163,6 +163,11 @@ protected EthereumTestResult RunTest(GeneralStateTest test, ITxTracer txTracer) testResult.TimeInMs = stopwatch.Elapsed.TotalMilliseconds; testResult.StateRoot = stateProvider.StateRoot; + foreach (string difference in differences) + { + TestContext.WriteLine(difference); + } + // Assert.Zero(differences.Count, "differences"); return testResult; } From 37fc94ff37b03f28d806c1a40ff09f4f35711fd8 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 10 Jul 2024 06:56:15 +0100 Subject: [PATCH 088/255] extcodehash is Keccak not Sha256 --- src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs | 6 ++++++ src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs index 99f7f874ba7..11a09faa975 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs @@ -163,6 +163,12 @@ protected EthereumTestResult RunTest(GeneralStateTest test, ITxTracer txTracer) testResult.TimeInMs = stopwatch.Elapsed.TotalMilliseconds; testResult.StateRoot = stateProvider.StateRoot; + if (differences.Count > 0) + { + TestContext.WriteLine(); + TestContext.WriteLine("Differences from expected"); + TestContext.WriteLine(); + } foreach (string difference in differences) { TestContext.WriteLine(difference); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 60dc3643ab6..50ecff3d630 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -37,6 +37,7 @@ public class VirtualMachine : IVirtualMachine { public const int MaxCallDepth = EvmObjectFormat.Eof1.RETURN_STACK_MAX_HEIGHT; private readonly static UInt256 P255Int = (UInt256)System.Numerics.BigInteger.Pow(2, 255); + internal readonly static byte[] EofHash256 = KeccakHash.ComputeHashBytes(EvmObjectFormat.MAGIC); internal static ref readonly UInt256 P255 => ref P255Int; internal static readonly UInt256 BigInt256 = 256; internal static readonly UInt256 BigInt32 = 32; @@ -2083,7 +2084,7 @@ private CallResult ExecuteCode account = _state.GetCode(address); if (spec.IsEofEnabled && EvmObjectFormat.IsEof(account, out _)) { - stack.PushBytes(SHA256.HashData(EvmObjectFormat.MAGIC)); + stack.PushBytes(EofHash256); } else { From d690c276e4e2cdf36bc5eabacff5f6e239a1eb6d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 10 Jul 2024 08:15:47 +0100 Subject: [PATCH 089/255] Fix extcodecopy_out_of_bounds --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 50ecff3d630..51499323974 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1432,7 +1432,7 @@ private CallResult ExecuteCode Date: Thu, 11 Jul 2024 01:28:47 +0100 Subject: [PATCH 090/255] Lookup code once --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 51499323974..f2c876ce838 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2767,6 +2767,7 @@ private EvmExceptionType InstructionEofCall Date: Thu, 11 Jul 2024 01:29:24 +0100 Subject: [PATCH 091/255] Use correct static flag --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f2c876ce838..94721e3d89b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2671,8 +2671,6 @@ private EvmExceptionType InstructionEofCall Date: Thu, 11 Jul 2024 01:29:42 +0100 Subject: [PATCH 092/255] Invert failure for EOF --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 94721e3d89b..cac620b6855 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -263,7 +263,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl return new TransactionSubstate(callResult.ExceptionType, isTracing); } - previousCallResult = StatusCode.FailureBytes; + previousCallResult = currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; previousCallOutputDestination = UInt256.Zero; _returnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; @@ -464,7 +464,8 @@ public TransactionSubstate Run(EvmState state, IWorldState worl else { _returnDataBuffer = callResult.Output.Bytes; - previousCallResult = callResult.PrecompileSuccess.HasValue + previousCallResult = previousState.ExecutionType.IsAnyCallEof() ? EofStatusCode.SuccessBytes : + 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)); From 3c203f3fb52960057418f40e80f0feabf9f25324 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 11 Jul 2024 09:27:08 +0100 Subject: [PATCH 093/255] Tidy up --- .../Nethermind.Core/Extensions/Bytes.cs | 38 +++---------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index 6761f43d671..71f5eb173a0 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -365,10 +365,6 @@ public static BigInteger ToUnsignedBigInteger(this ReadOnlySpan bytes) { return new(bytes, true, true); } - public static short ReadEthInt16(this Span bytes) - { - return ReadEthInt16((ReadOnlySpan)bytes); - } public static short ReadEthInt16(this ReadOnlySpan bytes) { @@ -385,11 +381,6 @@ public static short ReadEthInt16(this ReadOnlySpan bytes) }; } - public static ushort ReadEthUInt16(this Span bytes) - { - return ReadEthUInt16((ReadOnlySpan)bytes); - } - public static ushort ReadEthUInt16(this ReadOnlySpan bytes) { if (bytes.Length > 2) @@ -412,14 +403,12 @@ public static ushort ReadEthUInt16LittleEndian(this Span bytes) bytes = bytes.Slice(bytes.Length - 2, 2); } - if (bytes.Length == 2) + return bytes.Length switch { - return BinaryPrimitives.ReadUInt16LittleEndian(bytes); - } - - Span twoBytes = stackalloc byte[2]; - bytes.CopyTo(twoBytes[(2 - bytes.Length)..]); - return BinaryPrimitives.ReadUInt16LittleEndian(twoBytes); + 2 => BinaryPrimitives.ReadUInt16LittleEndian(bytes), + 1 => bytes[0], + _ => 0 + }; } public static uint ReadEthUInt32(this Span bytes) @@ -444,23 +433,6 @@ public static uint ReadEthUInt32(this ReadOnlySpan bytes) return BinaryPrimitives.ReadUInt32BigEndian(fourBytes); } - public static uint ReadEthUInt32LittleEndian(this Span bytes) - { - if (bytes.Length > 4) - { - bytes = bytes.Slice(bytes.Length - 4, 4); - } - - if (bytes.Length == 4) - { - return BinaryPrimitives.ReadUInt32LittleEndian(bytes); - } - - Span fourBytes = stackalloc byte[4]; - bytes.CopyTo(fourBytes[(4 - bytes.Length)..]); - return BinaryPrimitives.ReadUInt32LittleEndian(fourBytes); - } - public static int ReadEthInt32(this Span bytes) { return ReadEthInt32((ReadOnlySpan)bytes); From 207a05bacf152970776c506ec0ec66f9afc4e3d9 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 11 Jul 2024 23:34:37 +0100 Subject: [PATCH 094/255] fix EXTDELEGATECALL --- .../Nethermind.Evm/VirtualMachine.cs | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index cac620b6855..1bebac3f30f 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -132,7 +132,6 @@ private CallResult(EvmExceptionType exceptionType) ExceptionType = exceptionType; } - public EvmState? StateToExecute { get; } public (int? ContainerIndex, ReadOnlyMemory Bytes) Output { get; } public EvmExceptionType ExceptionType { get; } @@ -2477,11 +2476,11 @@ private EvmExceptionType InstructionCall( where TTracingInstructions : struct, IIsTracing where TTracingRefunds : struct, IIsTracing { + Metrics.IncrementCalls(); + returnData = null; ref readonly ExecutionEnvironment env = ref vmState.Env; - Metrics.IncrementCalls(); - if (instruction == Instruction.DELEGATECALL && !spec.DelegateCallEnabled || instruction == Instruction.STATICCALL && !spec.StaticCallEnabled) return EvmExceptionType.BadInstruction; @@ -2667,6 +2666,8 @@ private EvmExceptionType InstructionEofCall targetBytes); - stack.PopUInt256(out UInt256 dataOffset); - stack.PopUInt256(out UInt256 dataLength); + if (!stack.PopWord256(out Span targetBytes)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 dataOffset)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 dataLength)) return EvmExceptionType.StackUnderflow; UInt256 callValue; switch (instruction) @@ -2713,16 +2714,19 @@ private EvmExceptionType InstructionEofCall callData = vmState.Memory.Load(in dataOffset, dataLength); Snapshot snapshot = _state.TakeSnapshot(); - if (!callValue.IsZero) _state.SubtractFromBalance(caller, callValue, spec); + _state.SubtractFromBalance(caller, callValue, spec); ExecutionEnvironment callEnv = new ( txExecutionContext: in env.TxExecutionContext, callDepth: env.CallDepth + 1, caller: caller, - codeSource: targetAddress, - executingAccount: targetAddress, + codeSource: codeSource, + executingAccount: target, transferValue: callValue, value: callValue, inputData: callData, @@ -2804,7 +2808,7 @@ private EvmExceptionType InstructionEofCall Date: Fri, 12 Jul 2024 05:07:22 +0100 Subject: [PATCH 095/255] * minor refactor --- .../Nethermind.Evm/VirtualMachine.cs | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 1bebac3f30f..eb01d0d07c8 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1466,20 +1466,20 @@ private CallResult ExecuteCode _returnDataBuffer.Length) + if (env.CodeInfo.Version == 0 && UInt256.AddOverflow(c, b, out result) || result > _returnDataBuffer.Length) { goto AccessViolation; } - if (!result.IsZero) + if (!c.IsZero) { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, result)) goto OutOfGas; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, c)) goto OutOfGas; - slice = _returnDataBuffer.Span.SliceWithZeroPadding(b, (int)result); + slice = _returnDataBuffer.Span.SliceWithZeroPadding(b, (int)c); vmState.Memory.Save(in a, in slice); if (typeof(TTracingInstructions) == typeof(IsTracing)) { @@ -1827,7 +1827,7 @@ private CallResult ExecuteCode 0)) + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) goto InvalidInstruction; if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) @@ -1863,7 +1863,7 @@ private CallResult ExecuteCode 0)) + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) goto InvalidInstruction; if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) @@ -1877,7 +1877,7 @@ private CallResult ExecuteCode 0)) + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) goto InvalidInstruction; if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) @@ -1920,14 +1920,13 @@ private CallResult ExecuteCode 0)) + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) { goto InvalidInstruction; } if (vmState.IsStatic) goto StaticCallViolation; - UpdateCurrentState(vmState, programCounter, gasAvailable, stack.Head, sectionIndex); (exceptionType, returnData) = InstructionEofCreate(vmState, ref codeSection, ref stack, ref gasAvailable, spec, instruction); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; @@ -2211,7 +2210,7 @@ private CallResult ExecuteCode 0)) + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) { goto InvalidInstruction; } @@ -2242,7 +2241,7 @@ private CallResult ExecuteCode 0)) + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) { goto InvalidInstruction; } @@ -2262,7 +2261,7 @@ private CallResult ExecuteCode 0)) + if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) { goto InvalidInstruction; } @@ -2279,7 +2278,6 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; @@ -2681,9 +2679,9 @@ private EvmExceptionType InstructionEofCall targetBytes)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 dataOffset)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 dataLength)) return EvmExceptionType.StackUnderflow; + stack.PopWord256(out Span targetBytes); + stack.PopUInt256(out UInt256 dataOffset); + stack.PopUInt256(out UInt256 dataLength); UInt256 callValue; switch (instruction) @@ -2715,9 +2713,9 @@ private EvmExceptionType InstructionEofCall Date: Fri, 12 Jul 2024 05:29:39 +0100 Subject: [PATCH 096/255] * fix index out of range in EOFCREATE --- .../Nethermind.Evm/VirtualMachine.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index eb01d0d07c8..3c39bce648d 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1927,7 +1927,8 @@ private CallResult ExecuteCode 0) { if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; - if (!stack.PopUInt256(out a)) goto StackUnderflow; + + stack.PopUInt256(out a); var count = codeSection[programCounter] + 1; var immediates = (ushort)(count * EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.ONE_BYTE_LENGTH); if (a < count) @@ -2301,8 +2303,8 @@ private CallResult ExecuteCode auxData = ReadOnlyMemory.Empty; if (b > UInt256.Zero) { @@ -2335,7 +2337,7 @@ private CallResult ExecuteCode UInt256.Zero) { - if (!UpdateGas(GasCostOf.Memory + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable) || + if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable) || !UpdateMemoryCost(vmState, ref gasAvailable, in memOffset, size)) goto OutOfGas; @@ -2907,7 +2909,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref } [SkipLocalsInit] - private (EvmExceptionType exceptionType, EvmState? callState) InstructionEofCreate(EvmState vmState, ref ReadOnlySpan codeSection, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) + private (EvmExceptionType exceptionType, EvmState? callState) InstructionEofCreate(EvmState vmState, ref int pc, ref ReadOnlySpan codeSection, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) where TTracing : struct, IIsTracing { ref readonly ExecutionEnvironment env = ref vmState.Env; @@ -2919,7 +2921,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref return (EvmExceptionType.OutOfGas, null); // 2 - read immediate operand initcontainer_index, encoded as 8-bit unsigned value - int initcontainerIndex = codeSection[vmState.ProgramCounter++]; + int initcontainerIndex = codeSection[pc++]; // 3 - pop value, salt, input_offset, input_size from the operand stack // no stack checks becaue EOF guarantees no stack undeflows From 58c2168680a0899dcbaed7aeb170c393de514056 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 12 Jul 2024 11:19:32 +0100 Subject: [PATCH 097/255] formatting --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3c39bce648d..feedf5f6635 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2909,7 +2909,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref } [SkipLocalsInit] - private (EvmExceptionType exceptionType, EvmState? callState) InstructionEofCreate(EvmState vmState, ref int pc, ref ReadOnlySpan codeSection, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) + private (EvmExceptionType exceptionType, EvmState? callState) InstructionEofCreate(EvmState vmState, ref int pc, ref ReadOnlySpan codeSection, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) where TTracing : struct, IIsTracing { ref readonly ExecutionEnvironment env = ref vmState.Env; From af36d4ea2de9cc78f8429272917c34f3d9b61dc2 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 13 Jul 2024 03:25:15 +0100 Subject: [PATCH 098/255] Fix address extension checks --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index feedf5f6635..8fe8cb4a4bd 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2707,7 +2707,7 @@ private EvmExceptionType InstructionEofCall Date: Sat, 13 Jul 2024 05:11:35 +0100 Subject: [PATCH 099/255] Fix returndatacopy --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8fe8cb4a4bd..4f78aae7c3b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1470,7 +1470,7 @@ private CallResult ExecuteCode _returnDataBuffer.Length) + if (env.CodeInfo.Version == 0 && (UInt256.AddOverflow(c, b, out result) || result > _returnDataBuffer.Length)) { goto AccessViolation; } @@ -1749,14 +1749,13 @@ private CallResult ExecuteCode= codeSection.Length) + if (programCounter >= codeSection.Length) { stack.PushZero(); } else { - stack.PushByte(codeSection[programCounterInt]); + stack.PushByte(codeSection[programCounter]); } programCounter++; From f3b32ed6cde624ad2aab1535bc081176d8111d4a Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 15 Jul 2024 03:26:58 +0100 Subject: [PATCH 100/255] Update tests versions --- .../Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index dd45528a7a8..3580147a719 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.4" + ArchiveVersion = "eip7692@v1.0.5" }, $"fixtures/state_tests/prague"); return loader.LoadTests().Cast(); } From 71891af39b4cb464b9d50172ae3164e33fead357 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 15 Jul 2024 15:32:18 +0100 Subject: [PATCH 101/255] Include code for tx creates --- src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index 5244df1fde2..073264f65e9 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -23,9 +23,9 @@ public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec s return codeInfo; } - public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out ICodeInfo codeinfo, out Memory extraCalldata) + public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out ICodeInfo codeInfo, out Memory extraCalldata) { - codeinfo = new CodeInfo(data); + codeInfo = new CodeInfo(data); extraCalldata = default; if (spec.IsEofEnabled && data.Span.StartsWith(EvmObjectFormat.MAGIC)) { @@ -33,11 +33,12 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out { int containerSize = header.Value.DataSection.EndOffset; extraCalldata = data.Slice(containerSize); + codeInfo = new EofCodeInfo(codeInfo, header.Value); return true; } return false; } - codeinfo.AnalyseInBackgroundIfRequired(); + codeInfo.AnalyseInBackgroundIfRequired(); return true; } } From becbadec61c20628ee870da3a2adc3ad2d5b2f20 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 15 Jul 2024 15:33:26 +0100 Subject: [PATCH 102/255] Use bytecode --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 4f78aae7c3b..88b00b821ff 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -428,8 +428,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl { // 4 - set state[new_address].code to the updated deploy container // push new_address onto the stack (already done before the ifs) - ReadOnlyMemory code = callResult.Output.Bytes; - _codeInfoRepository.InsertCode(_state, code, callCodeOwner, spec); + _codeInfoRepository.InsertCode(_state, bytecodeResultArray, callCodeOwner, spec); currentState.GasAvailable -= codeDepositGasCost; if (_txTracer.IsTracingActions) From 6b8926d9166f299000c4df4d0eec8894fa08fe30 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 15 Jul 2024 23:00:22 +0100 Subject: [PATCH 103/255] * fix RETURNCONTRACT data section handling * Add missing EOFCREATE validation rule (RETURN/STOP and RETURNCONTRACT not in same container) --- .../EvmObjectFormat/EofCodeValidator.cs | 49 ++++++++++++++----- .../Nethermind.Evm/VirtualMachine.cs | 13 ++--- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 6188e7d83e3..9b6922be037 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -622,13 +622,12 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat } Span visitedSections = stackalloc bool[header.CodeSections.Count]; - Span visitedContainerSections = stackalloc byte[header.ContainerSections is null ? 0 : header.ContainerSections.Value.Count]; + Span visitedContainerSections = stackalloc byte[header.ContainerSections is null ? 1 : 1 + header.ContainerSections.Value.Count]; visitedSections.Clear(); Queue validationQueue = new Queue(); validationQueue.Enqueue(0); - while (validationQueue.TryDequeue(out ushort sectionIdx)) { if (visitedSections[sectionIdx]) @@ -648,7 +647,7 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat bool HasNoNonReachableSections = visitedSections[..header.CodeSections.Count].Contains(false) - || (header.ContainerSections is not null && visitedContainerSections[..header.ContainerSections.Value.Count].Contains((byte)0)); + || (header.ContainerSections is not null && visitedContainerSections[1..header.ContainerSections.Value.Count].Contains((byte)0)); return !HasNoNonReachableSections; } @@ -715,14 +714,42 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl Instruction opcode = (Instruction)code[pos]; int postInstructionByte = pos + 1; - if (strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) && opcode is Instruction.RETURN or Instruction.STOP) + if (opcode is Instruction.RETURN or Instruction.STOP) { - return false; + if(strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); + return false; + } else + { + if (visitedContainers[0] == (byte)ValidationStrategy.ValidateInitcodeMode) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); + return false; + } else + { + visitedContainers[0] = (byte)ValidationStrategy.ValidateRuntimeMode; + } + } } - if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) && opcode is Instruction.RETURNCONTRACT) + if (opcode is Instruction.RETURNCONTRACT) { - return false; + if(strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); + return false; + } else + { + if (visitedContainers[0] == (byte)ValidationStrategy.ValidateRuntimeMode) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); + return false; + } else + { + visitedContainers[0] = (byte)ValidationStrategy.ValidateInitcodeMode; + } + } } if (!opcode.IsValid(IsEofContext: true)) @@ -895,13 +922,13 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl return false; } - if (visitedContainers[runtimeContainerId] != 0 && visitedContainers[runtimeContainerId] != (byte)ValidationStrategy.ValidateRuntimeMode) + if (visitedContainers[runtimeContainerId + 1] != 0 && visitedContainers[runtimeContainerId + 1] != (byte)ValidationStrategy.ValidateRuntimeMode) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); return false; } - visitedContainers[runtimeContainerId] = (byte)ValidationStrategy.ValidateRuntimeMode; + visitedContainers[runtimeContainerId + 1] = (byte)ValidationStrategy.ValidateRuntimeMode; ReadOnlySpan subcontainer = container.Slice(header.ContainerSections.Value.Start + header.ContainerSections.Value[runtimeContainerId].Start, header.ContainerSections.Value[runtimeContainerId].Size); if (!IsValidEof(subcontainer, ValidationStrategy.ValidateRuntimeMode, out _)) @@ -930,13 +957,13 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl return false; } - if (visitedContainers[initcodeSectionId] != 0 && visitedContainers[initcodeSectionId] != (byte)ValidationStrategy.ValidateInitcodeMode) + if (visitedContainers[initcodeSectionId + 1] != 0 && visitedContainers[initcodeSectionId + 1] != (byte)ValidationStrategy.ValidateInitcodeMode) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s target container can only be a initcode mode bytecode"); return false; } - visitedContainers[initcodeSectionId] = (byte)ValidationStrategy.ValidateInitcodeMode; + visitedContainers[initcodeSectionId + 1] = (byte)ValidationStrategy.ValidateInitcodeMode; ReadOnlySpan subcontainer = container.Slice(header.ContainerSections.Value.Start + header.ContainerSections.Value[initcodeSectionId].Start, header.ContainerSections.Value[initcodeSectionId].Size); if (!IsValidEof(subcontainer, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateSubContainers | ValidationStrategy.ValidateFullBody, out _)) { diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 88b00b821ff..e62f6fe997d 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -396,6 +396,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl int deployContainerIndex = callResult.Output.ContainerIndex.Value; ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; ReadOnlySpan container = eofCodeInfo.ContainerSection.Span[(Range)eofCodeInfo.ContainerSectionOffset(deployContainerIndex).Value]; + EOF.EvmObjectFormat.IsValidEof(container, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader, out EOF.EofHeader? initcodeHeader); byte[] bytecodeResultArray = null; // 2 - concatenate data section with (aux_data_offset, aux_data_offset + aux_data_size) memory segment and update data size in the header @@ -409,15 +410,15 @@ public TransactionSubstate Run(EvmState state, IWorldState worl int dataSubheaderSectionStart = EvmObjectFormat.VERSION_OFFSET // magic + version + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) - + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) - + (eofCodeInfo.Header.ContainerSections is null + + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) + + (initcodeHeader?.ContainerSections is null ? 0 // container section : (0 bytes if no container section is available) - : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * initcodeHeader.Value.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator ushort dataSize = (ushort)(eofCodeInfo.DataSection.Length + auxExtraData.Length); - bytecodeResult[dataSubheaderSectionStart] = (byte)(bytecodeResult.Length >> 8); - bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(bytecodeResult.Length & 0xFF); + bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); + bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); bytecodeResultArray = bytecodeResult.ToArray(); @@ -2295,7 +2296,7 @@ private CallResult ExecuteCode Date: Mon, 15 Jul 2024 23:24:20 +0100 Subject: [PATCH 104/255] formatting --- .../EvmObjectFormat/EofCodeValidator.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 9b6922be037..9f73d29f4b0 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -716,17 +716,19 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl if (opcode is Instruction.RETURN or Instruction.STOP) { - if(strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode)) + if (strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); return false; - } else + } + else { if (visitedContainers[0] == (byte)ValidationStrategy.ValidateInitcodeMode) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); return false; - } else + } + else { visitedContainers[0] = (byte)ValidationStrategy.ValidateRuntimeMode; } @@ -735,17 +737,19 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl if (opcode is Instruction.RETURNCONTRACT) { - if(strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) + if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); return false; - } else + } + else { if (visitedContainers[0] == (byte)ValidationStrategy.ValidateRuntimeMode) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); return false; - } else + } + else { visitedContainers[0] = (byte)ValidationStrategy.ValidateInitcodeMode; } From 4104ebeb49f32d3690c386ea9c56cd4624e11ca2 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 15 Jul 2024 23:31:22 +0100 Subject: [PATCH 105/255] * Fix to RETURNCONTRACT targeting wrong container --- .../Nethermind.Evm/VirtualMachine.cs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index e62f6fe997d..9163849f3d1 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -113,10 +113,10 @@ public CallResult(ReadOnlyMemory output, bool? precompileSuccess, int from FromVersion = fromVersion; } - public CallResult(int containerIndex, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + public CallResult(ICodeInfo container, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) { StateToExecute = null; - Output = (containerIndex, output); + Output = (container, output); PrecompileSuccess = precompileSuccess; ShouldRevert = shouldRevert; ExceptionType = exceptionType; @@ -133,7 +133,7 @@ private CallResult(EvmExceptionType exceptionType) } public EvmState? StateToExecute { get; } - public (int? ContainerIndex, ReadOnlyMemory Bytes) Output { get; } + public (ICodeInfo Container, ReadOnlyMemory Bytes) Output { get; } public EvmExceptionType ExceptionType { get; } public bool ShouldRevert { get; } public bool? PrecompileSuccess { get; } // TODO: check this behaviour as it seems it is required and previously that was not the case @@ -393,30 +393,28 @@ public TransactionSubstate Run(EvmState state, IWorldState worl EofCodeInfo eofCodeInfo = (EofCodeInfo)previousState.Env.CodeInfo; // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed - int deployContainerIndex = callResult.Output.ContainerIndex.Value; ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; - ReadOnlySpan container = eofCodeInfo.ContainerSection.Span[(Range)eofCodeInfo.ContainerSectionOffset(deployContainerIndex).Value]; - EOF.EvmObjectFormat.IsValidEof(container, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader, out EOF.EofHeader? initcodeHeader); + EofCodeInfo deployCodeInfo = (EofCodeInfo)callResult.Output.Container; byte[] bytecodeResultArray = null; // 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[container.Length + auxExtraData.Length]; + Span bytecodeResult = new byte[deployCodeInfo.MachineCode.Length + auxExtraData.Length]; // 2 - 1 - 1 - copy old container - container.CopyTo(bytecodeResult); + deployCodeInfo.MachineCode.Span.CopyTo(bytecodeResult); // 2 - 1 - 2 - copy aux data to dataSection - auxExtraData.CopyTo(bytecodeResult[container.Length..]); + auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.Length..]); // 2 - 2 - update data section size in the header u16 int dataSubheaderSectionStart = EvmObjectFormat.VERSION_OFFSET // magic + version + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) - + (initcodeHeader?.ContainerSections is null + + (deployCodeInfo.Header.ContainerSections is null ? 0 // container section : (0 bytes if no container section is available) - : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * initcodeHeader.Value.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator - ushort dataSize = (ushort)(eofCodeInfo.DataSection.Length + auxExtraData.Length); + ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); @@ -2302,14 +2300,18 @@ private CallResult ExecuteCode deployCode = env.CodeInfo.ContainerSection[(Range)env.CodeInfo.ContainerSectionOffset(sectionIdx)]; + EofCodeInfo deploycodeInfo = (EofCodeInfo)CodeInfoFactory.CreateCodeInfo(deployCode, spec, EvmObjectFormat.ValidationStrategy.ExractHeader); + stack.PopUInt256(out a); stack.PopUInt256(out b); ReadOnlyMemory auxData = ReadOnlyMemory.Empty; + if (b > UInt256.Zero) { if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; - if (((int)b + dataSection.Length) != (env.CodeInfo as EofCodeInfo).Header.DataSection.Size) + if (((int)b + deploycodeInfo.DataSection.Length) != deploycodeInfo .Header.DataSection.Size) { goto AccessViolation; } @@ -2317,7 +2319,7 @@ private CallResult ExecuteCode Date: Mon, 15 Jul 2024 23:49:25 +0100 Subject: [PATCH 106/255] * Fix overflow and underflow check in RETURNCONTRACT --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 9163849f3d1..2faf03da782 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2311,7 +2311,8 @@ private CallResult ExecuteCode UInt16.MaxValue) { goto AccessViolation; } From a81e2b67b2c97ccc179e928d1ca09cfd09004d0e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 16 Jul 2024 01:27:12 +0100 Subject: [PATCH 107/255] * Aligned TxCreate with EOFCREATE --- src/Nethermind/Nethermind.Core/Transaction.cs | 3 + .../TransactionProcessor.cs | 62 ++++++++++++++++--- .../Nethermind.Evm/TransactionSubstate.cs | 9 +-- .../Nethermind.Evm/VirtualMachine.cs | 4 +- 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index 1371eba41de..6725f53cf24 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -19,6 +19,7 @@ namespace Nethermind.Core [DebuggerDisplay("{Hash}, Value: {Value}, To: {To}, Gas: {GasLimit}")] public class Transaction { + public byte[] EofMagic = [0xEF, 0x00]; public const int BaseTxGasCost = 21000; public ulong? ChainId { get; set; } @@ -53,6 +54,8 @@ 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) ?? false); + public bool IsLegacyContractCreation => IsContractCreation && !IsEofContractCreation; public bool IsMessageCall => To is not null; private Hash256? _hash; diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 0bd1e3d64f3..7585341f628 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -175,13 +175,13 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon if (statusCode == StatusCode.Failure) { - byte[] output = (substate?.ShouldRevert ?? false) ? substate.Output.ToArray() : Array.Empty(); + byte[] output = (substate?.ShouldRevert ?? false) ? substate.Output.Bytes.ToArray() : Array.Empty(); tracer.MarkAsFailed(env.Value.ExecutingAccount, spentGas, output, substate?.Error, stateRoot); } else { LogEntry[] logs = substate.Logs.Count != 0 ? substate.Logs.ToArray() : Array.Empty(); - tracer.MarkAsSuccess(env.Value.ExecutingAccount, spentGas, substate.Output.ToArray(), logs, stateRoot); + tracer.MarkAsSuccess(env.Value.ExecutingAccount, spentGas, substate.Output.Bytes.ToArray(), logs, stateRoot); } } @@ -487,7 +487,7 @@ protected void ExecuteEvmCall( PrepareAccountForContractDeployment(env.ExecutingAccount, spec); } - ExecutionType executionType = tx.IsContractCreation ? ExecutionType.CREATE : ExecutionType.TRANSACTION; + ExecutionType executionType = tx.IsContractCreation ? (tx.IsEofContractCreation ? ExecutionType.TXCREATE : ExecutionType.CREATE) : ExecutionType.TRANSACTION; using (EvmState state = new(unspentGas, env, executionType, true, snapshot, false)) { @@ -529,29 +529,75 @@ protected void ExecuteEvmCall( { // tks: there is similar code fo contract creation from init and from CREATE // this may lead to inconsistencies (however it is tested extensively in blockchain tests) - if (tx.IsContractCreation) + if (tx.IsLegacyContractCreation) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Length); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Bytes.Length); if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) { ThrowOutOfGasException(); } - // is the new txType considered a contractCreation if it needs CREATE4 to function as such? - if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output, 0)) + if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output.Bytes, 0)) { ThrowInvalidCodeException(); } if (unspentGas >= codeDepositGasCost) { - var code = substate.Output.ToArray(); + var code = substate.Output.Bytes.ToArray(); _codeInfoRepository.InsertCode(WorldState, code, env.ExecutingAccount, spec); unspentGas -= codeDepositGasCost; } } + if (tx.IsEofContractCreation) + { + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Bytes.Length); + if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) + { + ThrowOutOfGasException(); + } + + // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed + ReadOnlySpan auxExtraData = substate.Output.Bytes.Span; + EofCodeInfo deployCodeInfo = (EofCodeInfo)substate.Output.DeployCode; + byte[] bytecodeResultArray = null; + + // 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.MachineCode.Length + auxExtraData.Length]; + // 2 - 1 - 1 - copy old container + deployCodeInfo.MachineCode.Span.CopyTo(bytecodeResult); + // 2 - 1 - 2 - copy aux data to dataSection + auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.Length..]); + + // 2 - 2 - update data section size in the header u16 + int dataSubheaderSectionStart = + EvmObjectFormat.VERSION_OFFSET // magic + version + + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) + + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) + + (deployCodeInfo.Header.ContainerSections is null + ? 0 // container section : (0 bytes if no container section is available) + : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator + + ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); + bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); + bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); + + bytecodeResultArray = bytecodeResult.ToArray(); + + // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts + bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); + if (unspentGas >= codeDepositGasCost && !invalidCode) + { + // 4 - set state[new_address].code to the updated deploy container + // push new_address onto the stack (already done before the ifs) + _codeInfoRepository.InsertCode(WorldState, bytecodeResultArray, env.ExecutingAccount, spec); + unspentGas -= codeDepositGasCost; + } + } + foreach (Address toBeDestroyed in substate.DestroyList) { if (Logger.IsTrace) diff --git a/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs b/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs index bfc253e6844..c5d6415a4be 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs @@ -9,6 +9,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; +using Nethermind.Evm.CodeAnalysis; using Nethermind.Int256; using Nethermind.Logging; @@ -46,7 +47,7 @@ public class TransactionSubstate public bool IsError => Error is not null && !ShouldRevert; public string? Error { get; } - public ReadOnlyMemory Output { get; } + public (ICodeInfo DeployCode, ReadOnlyMemory Bytes) Output { get; } public bool ShouldRevert { get; } public long Refund { get; } public IReadOnlyCollection Logs { get; } @@ -61,7 +62,7 @@ public TransactionSubstate(EvmExceptionType exceptionType, bool isTracerConnecte ShouldRevert = false; } - public TransactionSubstate(ReadOnlyMemory output, + public TransactionSubstate((ICodeInfo eofDeployCode, ReadOnlyMemory bytes) output, long refund, IReadOnlyCollection
destroyList, IReadOnlyCollection logs, @@ -87,10 +88,10 @@ public TransactionSubstate(ReadOnlyMemory output, if (!isTracerConnected) return; - if (Output.Length <= 0) + if (Output.Bytes.Length <= 0) return; - ReadOnlySpan span = Output.Span; + ReadOnlySpan span = Output.Bytes.Span; Error = string.Concat( RevertedErrorMessagePrefix, TryGetErrorMessage(span) ?? EncodeErrorMessage(span) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 2faf03da782..7e77dc554ce 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -324,7 +324,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } return new TransactionSubstate( - callResult.Output.Bytes, + callResult.Output, currentState.Refund, (IReadOnlyCollection
)currentState.DestroyList, (IReadOnlyCollection)currentState.Logs, @@ -408,7 +408,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl int dataSubheaderSectionStart = EvmObjectFormat.VERSION_OFFSET // magic + version + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) - + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * eofCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) + + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) + (deployCodeInfo.Header.ContainerSections is null ? 0 // container section : (0 bytes if no container section is available) : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) From ebf4a82af27a9e591d4793c2b3c6299eb826ec31 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 16 Jul 2024 01:32:42 +0100 Subject: [PATCH 108/255] * fix failing build test --- .../Nethermind.Evm.Test/TransactionSubstateTests.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionSubstateTests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionSubstateTests.cs index 3b44d847768..db8fcc82c0c 100644 --- a/src/Nethermind/Nethermind.Evm.Test/TransactionSubstateTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionSubstateTests.cs @@ -7,6 +7,7 @@ using FluentAssertions; using Nethermind.Core; using Nethermind.Core.Extensions; +using Nethermind.Evm.CodeAnalysis; using NUnit.Framework; namespace Nethermind.Evm.Test @@ -26,7 +27,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(readOnlyMemory, + TransactionSubstate transactionSubstate = new((CodeInfo.Empty, readOnlyMemory), 0, new ArraySegment
(), new LogEntry[] { }, @@ -40,7 +41,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(readOnlyMemory, + TransactionSubstate transactionSubstate = new((CodeInfo.Empty, readOnlyMemory), 0, new ArraySegment
(), new LogEntry[] { }, @@ -54,7 +55,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(readOnlyMemory, + TransactionSubstate transactionSubstate = new((CodeInfo.Empty, readOnlyMemory), 0, new ArraySegment
(), new LogEntry[] { }, @@ -74,7 +75,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( - readOnlyMemory, + (CodeInfo.Empty, readOnlyMemory), 0, new ArraySegment
(), new LogEntry[] { }, @@ -147,7 +148,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( - readOnlyMemory, + (CodeInfo.Empty, readOnlyMemory), 0, new ArraySegment
(), new LogEntry[] { }, @@ -171,7 +172,7 @@ public void should_return_proper_revert_error_when_revert_custom_error() }; ReadOnlyMemory readOnlyMemory = new(data); TransactionSubstate transactionSubstate = new( - readOnlyMemory, + (CodeInfo.Empty, readOnlyMemory), 0, new ArraySegment
(), new LogEntry[] { }, From 241f09521c5060791a4d789c13feab721922af56 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 16 Jul 2024 14:36:10 +0100 Subject: [PATCH 109/255] * fix CREATETX deploy gas value --- .../TransactionProcessing/TransactionProcessor.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 7585341f628..4bb1c96a2a8 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -553,15 +553,16 @@ protected void ExecuteEvmCall( if (tx.IsEofContractCreation) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Bytes.Length); + // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed + ReadOnlySpan auxExtraData = substate.Output.Bytes.Span; + EofCodeInfo deployCodeInfo = (EofCodeInfo)substate.Output.DeployCode; + + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, deployCodeInfo.MachineCode.Length + auxExtraData.Length); if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) { ThrowOutOfGasException(); } - // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed - ReadOnlySpan auxExtraData = substate.Output.Bytes.Span; - EofCodeInfo deployCodeInfo = (EofCodeInfo)substate.Output.DeployCode; byte[] bytecodeResultArray = null; // 2 - concatenate data section with (aux_data_offset, aux_data_offset + aux_data_size) memory segment and update data size in the header From bbf305e38e9e8287cbeda9dd85663145e2e60e51 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jul 2024 06:00:14 +0100 Subject: [PATCH 110/255] * Add EofBlockChainTests --- .../PragueBlockchainTests.cs | 29 +++++++++++++++++++ .../Nethermind.Evm/VirtualMachine.cs | 6 ++-- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs new file mode 100644 index 00000000000..322b08f0f59 --- /dev/null +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ethereum.Test.Base; +using FluentAssertions; +using NUnit.Framework; + +namespace Ethereum.Blockchain.Pyspec.Test; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class PrageBlockChainTests : BlockchainTestBase +{ + [TestCaseSource(nameof(LoadTests))] + public async Task Test(BlockchainTest test) => await RunTest(test); + + private static IEnumerable LoadTests() + { + TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() + { + ArchiveName = "fixtures_eip7692.tar.gz", + ArchiveVersion = "eip7692@v1.0.5" + }, $"fixtures/blockchain_tests/prague"); + return loader.LoadTests().Cast(); + } +} diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 7e77dc554ce..3dcf475c1e5 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -389,9 +389,6 @@ public TransactionSubstate Run(EvmState state, IWorldState worl else if (previousState.ExecutionType.IsAnyCreateEof()) { // ReturnContract was called with a container index and auxdata - - EofCodeInfo eofCodeInfo = (EofCodeInfo)previousState.Env.CodeInfo; - // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; EofCodeInfo deployCodeInfo = (EofCodeInfo)callResult.Output.Container; @@ -2941,7 +2938,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref int initcontainerSize = container.Header.ContainerSections.Value[initcontainerIndex].Size; // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) - if (!UpdateGas(GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize), ref gasAvailable)) + long hashCost = (GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize)); + if (!UpdateGas(hashCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); // 7 - check that current call depth is below STACK_DEPTH_LIMIT and that caller balance is enough to transfer value From 744bf19c6d3328ed6f43158f720a90ebc64c5d9c Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 17 Jul 2024 13:23:25 +0100 Subject: [PATCH 111/255] Include more detail in logs --- src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs | 2 +- src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 132c8c4bb2f..1623b020031 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -44,7 +44,7 @@ public abstract class BlockchainTestBase { private static InterfaceLogger _logger = new NUnitLogger(LogLevel.Trace); // private static ILogManager _logManager = new OneLoggerLogManager(_logger); - private static ILogManager _logManager = LimboLogs.Instance; + private static ILogManager _logManager = new TestLogManager(LogLevel.Warn); private static ISealValidator Sealer { get; } private static DifficultyCalculatorWrapper DifficultyCalculator { get; } diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs index 11a09faa975..6c4abeba1fb 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs @@ -32,7 +32,7 @@ namespace Ethereum.Test.Base public abstract class GeneralStateTestBase { private static ILogger _logger = new(new ConsoleAsyncLogger(LogLevel.Info)); - private static ILogManager _logManager = LimboLogs.Instance; + private static ILogManager _logManager = new TestLogManager(LogLevel.Warn); private static readonly UInt256 _defaultBaseFeeForStateTest = 0xA; private readonly TxValidator _txValidator = new(MainnetSpecProvider.Instance.ChainId); From cbdbea78194e8aea9660c1b423daed3e7bad6427 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 17 Jul 2024 13:34:11 +0100 Subject: [PATCH 112/255] Fix DATACOPY gas --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3dcf475c1e5..9306395c50a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2364,9 +2364,12 @@ private CallResult ExecuteCode UInt256.Zero) { - if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable) || + if (!UpdateGas(GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable) || !UpdateMemoryCost(vmState, ref gasAvailable, in memOffset, size)) goto OutOfGas; @@ -2938,7 +2941,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref int initcontainerSize = container.Header.ContainerSections.Value[initcontainerIndex].Size; // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) - long hashCost = (GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize)); + long hashCost = (GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize)); if (!UpdateGas(hashCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); From f9aa9c5d3e9ec62211602a3271a863e5e6d04239 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 17 Jul 2024 13:38:54 +0100 Subject: [PATCH 113/255] better fix --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 9306395c50a..592f38b7f28 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2364,13 +2364,12 @@ private CallResult ExecuteCode UInt256.Zero) { - if (!UpdateGas(GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable) || - !UpdateMemoryCost(vmState, ref gasAvailable, in memOffset, size)) + if (!UpdateMemoryCost(vmState, ref gasAvailable, in memOffset, size)) goto OutOfGas; ZeroPaddedSpan dataSectionSlice = dataSection.SliceWithZeroPadding(offset, (int)size); From 0f435150369e441fc8b3cf9d1e9a6dcfe59c583b Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jul 2024 14:46:15 +0100 Subject: [PATCH 114/255] * remove unnecessary validation --- .../Nethermind.Evm/CodeInfoFactory.cs | 5 ++- .../EvmObjectFormat/EofCodeValidator.cs | 37 +++++++------------ .../Nethermind.Evm/VirtualMachine.cs | 4 +- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index 073264f65e9..f8be7eb0a05 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -29,11 +29,12 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out extraCalldata = default; if (spec.IsEofEnabled && data.Span.StartsWith(EvmObjectFormat.MAGIC)) { - if (EvmObjectFormat.IsValidEof(data.Span, EvmObjectFormat.ValidationStrategy.ValidateInitcodeMode | EvmObjectFormat.ValidationStrategy.ValidateFullBody | EvmObjectFormat.ValidationStrategy.ValidateSubContainers | EvmObjectFormat.ValidationStrategy.AllowTrailingBytes, out EofHeader? header)) + if (EvmObjectFormat.IsValidEof(data.Span, EvmObjectFormat.ValidationStrategy.ValidateInitcodeMode | EvmObjectFormat.ValidationStrategy.ValidateFullBody | EvmObjectFormat.ValidationStrategy.AllowTrailingBytes, out EofHeader? header)) { int containerSize = header.Value.DataSection.EndOffset; extraCalldata = data.Slice(containerSize); - codeInfo = new EofCodeInfo(codeInfo, header.Value); + ICodeInfo innerCodeInfo = new CodeInfo(data.Slice(0, containerSize)); + codeInfo = new EofCodeInfo(innerCodeInfo, header.Value); return true; } return false; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 9f73d29f4b0..dc4d06c5d99 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -56,13 +56,12 @@ public enum ValidationStrategy { None = 0, Validate = 1, - ValidateSubContainers = Validate | 2, - ValidateFullBody = Validate | 4, - ValidateInitcodeMode = Validate | 8, - ValidateRuntimeMode = Validate | 16, - AllowTrailingBytes = Validate | 32, - ExractHeader = 64, - HasEofMagic = 128, + ValidateFullBody = Validate | 2, + ValidateInitcodeMode = Validate | 4, + ValidateRuntimeMode = Validate | 8, + AllowTrailingBytes = Validate | 16, + ExractHeader = 32, + HasEofMagic = 64, } @@ -124,23 +123,9 @@ public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy s && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) && handler.TryParseEofHeader(container, out header)) { - bool validateBody = true || strategy.HasFlag(ValidationStrategy.Validate); + bool validateBody = strategy.HasFlag(ValidationStrategy.Validate); if (validateBody && handler.ValidateBody(container, header.Value, strategy)) { - if (strategy.HasFlag(ValidationStrategy.ValidateSubContainers) && header?.ContainerSections?.Count > 0) - { - int containerSize = header.Value.ContainerSections.Value.Count; - - for (int i = 0; i < containerSize; i++) - { - ReadOnlySpan subContainer = container.Slice(header.Value.ContainerSections.Value.Start + header.Value.ContainerSections.Value[i].Start, header.Value.ContainerSections.Value[i].Size); - if (!IsValidEof(subContainer, ValidationStrategy.Validate, out _)) - { - return false; - } - } - return true; - } return true; } return !validateBody; @@ -623,6 +608,12 @@ public bool ValidateBody(ReadOnlySpan container, EofHeader header, Validat Span visitedSections = stackalloc bool[header.CodeSections.Count]; Span visitedContainerSections = stackalloc byte[header.ContainerSections is null ? 1 : 1 + header.ContainerSections.Value.Count]; + visitedContainerSections[0] = strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) + ? (byte)ValidationStrategy.ValidateInitcodeMode + : (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) + ? (byte)ValidationStrategy.ValidateRuntimeMode + : (byte)0); + visitedSections.Clear(); Queue validationQueue = new Queue(); @@ -969,7 +960,7 @@ bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnl visitedContainers[initcodeSectionId + 1] = (byte)ValidationStrategy.ValidateInitcodeMode; ReadOnlySpan subcontainer = container.Slice(header.ContainerSections.Value.Start + header.ContainerSections.Value[initcodeSectionId].Start, header.ContainerSections.Value[initcodeSectionId].Size); - if (!IsValidEof(subcontainer, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateSubContainers | ValidationStrategy.ValidateFullBody, out _)) + if (!IsValidEof(subcontainer, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateFullBody, out _)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must be a valid Eof"); return false; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 592f38b7f28..a554d10352d 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2304,9 +2304,9 @@ private CallResult ExecuteCode auxData = ReadOnlyMemory.Empty; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; if (b > UInt256.Zero) { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; int projectedNewSize = (int)b + deploycodeInfo.DataSection.Length; if (projectedNewSize < deploycodeInfo.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) @@ -2940,7 +2940,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref int initcontainerSize = container.Header.ContainerSections.Value[initcontainerIndex].Size; // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) - long hashCost = (GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize)); + long hashCost = GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize); if (!UpdateGas(hashCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); From 91063aaf8072c4e150ec028e9ba70ceec66434ca Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jul 2024 15:11:10 +0100 Subject: [PATCH 115/255] * perform datasize overflow and underflow in ReturnContract regardless of inputs --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index a554d10352d..396fc4b627b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2305,18 +2305,15 @@ private CallResult ExecuteCode auxData = ReadOnlyMemory.Empty; if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; - if (b > UInt256.Zero) - { - - int projectedNewSize = (int)b + deploycodeInfo.DataSection.Length; - if (projectedNewSize < deploycodeInfo.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) - { - goto AccessViolation; - } - auxData = vmState.Memory.Load(a, b); + int projectedNewSize = (int)b + deploycodeInfo.DataSection.Length; + if (projectedNewSize < deploycodeInfo.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) + { + goto AccessViolation; } + auxData = vmState.Memory.Load(a, b); + return new CallResult(deploycodeInfo, auxData, null, env.CodeInfo.Version); } case Instruction.DATASIZE: From a3b2292c79c8aed95793704f1593842b8a35117b Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 17 Jul 2024 15:19:40 +0100 Subject: [PATCH 116/255] Max container size is MAX_INITCODE_SIZE --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 396fc4b627b..e8ebec663cb 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2291,6 +2291,9 @@ private CallResult ExecuteCode UInt16.MaxValue) + if (projectedNewSize < deploycodeInfo.Header.DataSection.Size || projectedNewSize > MAX_INITCODE_SIZE) { goto AccessViolation; } From d9ed010dd4180dbdf32cb591169f72c56ac69b6d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jul 2024 15:19:41 +0100 Subject: [PATCH 117/255] * fix maxCodeSize check --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 396fc4b627b..d29b8657b91 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -418,7 +418,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl bytecodeResultArray = bytecodeResult.ToArray(); // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); + bool invalidCode = !(bytecodeResultArray.Length <= spec.MaxCodeSize); long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, bytecodeResultArray?.Length ?? 0); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { From 794b1dc87db16ec83aefd0d1e2f29266afdb2661 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 17 Jul 2024 15:34:35 +0100 Subject: [PATCH 118/255] Use section max size --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index c43fddaa89b..d29b8657b91 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2291,9 +2291,6 @@ private CallResult ExecuteCode MAX_INITCODE_SIZE) + if (projectedNewSize < deploycodeInfo.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) { goto AccessViolation; } From 5f3d9c314fd734e03db01b3fa236fe3b3792a596 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jul 2024 15:37:05 +0100 Subject: [PATCH 119/255] * Added Eip3860 check and cost --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index c43fddaa89b..79ba6828090 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2938,9 +2938,17 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header ReadOnlySpan initcontainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initcontainerIndex).Value]; int initcontainerSize = container.Header.ContainerSections.Value[initcontainerIndex].Size; + long numberOfWordInInitcode = EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize); + // Eip3860 + if (spec.IsEip3860Enabled) + { + if(!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) + return (EvmExceptionType.OutOfGas, null); + if (initcontainerSize > spec.MaxInitCodeSize) return (EvmExceptionType.OutOfGas, null); + } // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) - long hashCost = GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize); + long hashCost = GasCostOf.Sha3Word * numberOfWordInInitcode; if (!UpdateGas(hashCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); From a9985d01783e21b5498bbb68e96f274231293a5f Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Jul 2024 15:39:47 +0100 Subject: [PATCH 120/255] * remove Eip3860 check --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index bd3f97bddbe..b87bda35fcc 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2937,12 +2937,12 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref int initcontainerSize = container.Header.ContainerSections.Value[initcontainerIndex].Size; long numberOfWordInInitcode = EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize); // Eip3860 - if (spec.IsEip3860Enabled) - { - if(!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) - return (EvmExceptionType.OutOfGas, null); - if (initcontainerSize > spec.MaxInitCodeSize) return (EvmExceptionType.OutOfGas, null); - } + // if (spec.IsEip3860Enabled) + // { + // if(!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) + // return (EvmExceptionType.OutOfGas, null); + // if (initcontainerSize > spec.MaxInitCodeSize) return (EvmExceptionType.OutOfGas, null); + // } // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) long hashCost = GasCostOf.Sha3Word * numberOfWordInInitcode; From 7547caa32ecb551aab8bbe0ec930391b8ecd71a0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 17 Jul 2024 15:47:56 +0100 Subject: [PATCH 121/255] Tidy up --- .../Nethermind.Evm/VirtualMachine.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index b87bda35fcc..8f45a0bb9bf 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2933,19 +2933,18 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header - ReadOnlySpan initcontainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initcontainerIndex).Value]; - int initcontainerSize = container.Header.ContainerSections.Value[initcontainerIndex].Size; - long numberOfWordInInitcode = EvmPooledMemory.Div32Ceiling((UInt256)initcontainerSize); + ReadOnlySpan initContainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initcontainerIndex).Value]; // Eip3860 - // if (spec.IsEip3860Enabled) - // { - // if(!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) - // return (EvmExceptionType.OutOfGas, null); - // if (initcontainerSize > spec.MaxInitCodeSize) return (EvmExceptionType.OutOfGas, null); - // } + if (spec.IsEip3860Enabled) + { + //if (!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) + // return (EvmExceptionType.OutOfGas, null); + if (initContainer.Length > spec.MaxInitCodeSize) return (EvmExceptionType.OutOfGas, null); + } // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) - long hashCost = GasCostOf.Sha3Word * numberOfWordInInitcode; + long numberOfWordsInInitCode = EvmPooledMemory.Div32Ceiling((UInt256)initContainer.Length); + long hashCost = GasCostOf.Sha3Word * numberOfWordsInInitCode; if (!UpdateGas(hashCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); @@ -2979,7 +2978,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref _state.IncrementNonce(env.ExecutingAccount); // 11 - calculate new_address as keccak256(0xff || sender || salt || keccak256(initcontainer))[12:] - Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initcontainer); + Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initContainer); if (spec.UseHotAndColdStorage) { // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 @@ -3011,7 +3010,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initcontainer.ToArray(), spec, EvmObjectFormat.ValidationStrategy.ExractHeader); + ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initContainer.ToArray(), spec, EvmObjectFormat.ValidationStrategy.ExractHeader); ExecutionEnvironment callEnv = new ( From 38f378f3f3440edcc7acd6dd3f295fe841349651 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 18 Jul 2024 02:28:12 +0100 Subject: [PATCH 122/255] Add new tests --- .../Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs index 322b08f0f59..e0db71c5c14 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -22,7 +22,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.5" + ArchiveVersion = "eip7692@v1.0.6" }, $"fixtures/blockchain_tests/prague"); return loader.LoadTests().Cast(); } diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index 3580147a719..f2576e28b3b 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.5" + ArchiveVersion = "eip7692@v1.0.6" }, $"fixtures/state_tests/prague"); return loader.LoadTests().Cast(); } From e19be6560f720fc1b813b8ef0705264ae20e00ee Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 18 Jul 2024 06:06:17 +0100 Subject: [PATCH 123/255] Fix initcode failure states --- .../Nethermind.Core/Buffers/CappedArray.cs | 13 + .../Nethermind.Evm/CodeInfoFactory.cs | 3 +- .../TransactionProcessor.cs | 232 +++++++++--------- .../Nethermind.Evm/TransactionSubstate.cs | 11 + .../Nethermind.State/StateProvider.cs | 15 +- .../Nethermind.Trie/PatriciaTree.cs | 2 +- 6 files changed, 151 insertions(+), 125 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Buffers/CappedArray.cs b/src/Nethermind/Nethermind.Core/Buffers/CappedArray.cs index 9dfa54cf9a0..6658f55edfd 100644 --- a/src/Nethermind/Nethermind.Core/Buffers/CappedArray.cs +++ b/src/Nethermind/Nethermind.Core/Buffers/CappedArray.cs @@ -4,6 +4,8 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using Nethermind.Core.Extensions; namespace Nethermind.Core.Buffers; @@ -14,6 +16,7 @@ namespace Nethermind.Core.Buffers; /// if it represent null. ///
public readonly struct CappedArray + where T : struct { private readonly static CappedArray _null = default; private readonly static CappedArray _empty = new CappedArray(Array.Empty()); @@ -111,6 +114,16 @@ public readonly Span AsSpan(int start, int length) return AsSpan().ToArray(); } + public override string? ToString() + { + if (typeof(T) == typeof(byte)) + { + return SpanExtensions.ToHexString(MemoryMarshal.AsBytes(AsSpan()), withZeroX: true); + } + + return base.ToString(); + } + public readonly ArraySegment AsArraySegment() { return AsArraySegment(0, _length); diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index f8be7eb0a05..77e74f670f3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -25,7 +25,6 @@ public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec s public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out ICodeInfo codeInfo, out Memory extraCalldata) { - codeInfo = new CodeInfo(data); extraCalldata = default; if (spec.IsEofEnabled && data.Span.StartsWith(EvmObjectFormat.MAGIC)) { @@ -37,8 +36,10 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out codeInfo = new EofCodeInfo(innerCodeInfo, header.Value); return true; } + codeInfo = null; return false; } + codeInfo = new CodeInfo(data); codeInfo.AnalyseInBackgroundIfRequired(); return true; } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 4bb1c96a2a8..6fbbe3dcd04 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -133,12 +133,12 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon if (commit) WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullTxTracer.Instance, commitStorageRoots: false); long gasAvailable = tx.GasLimit - intrinsicGas; - if (!(result = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, out ExecutionEnvironment? env))) + if (!(result = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, out ExecutionEnvironment env))) { return result; } - ExecuteEvmCall(tx, header, spec, tracer, opts, gasAvailable, env.Value, out TransactionSubstate? substate, out long spentGas, out byte statusCode); + ExecuteEvmCall(tx, header, spec, tracer, opts, gasAvailable, in env, out TransactionSubstate? substate, out long spentGas, out byte statusCode); PayFees(tx, header, spec, tracer, substate, spentGas, premiumPerGas, statusCode); // Finalize @@ -176,12 +176,12 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon if (statusCode == StatusCode.Failure) { byte[] output = (substate?.ShouldRevert ?? false) ? substate.Output.Bytes.ToArray() : Array.Empty(); - tracer.MarkAsFailed(env.Value.ExecutingAccount, spentGas, output, substate?.Error, stateRoot); + tracer.MarkAsFailed(env.ExecutingAccount, spentGas, output, substate?.Error, stateRoot); } else { LogEntry[] logs = substate.Logs.Count != 0 ? substate.Logs.ToArray() : Array.Empty(); - tracer.MarkAsSuccess(env.Value.ExecutingAccount, spentGas, substate.Output.Bytes.ToArray(), logs, stateRoot); + tracer.MarkAsSuccess(env.ExecutingAccount, spentGas, substate.Output.Bytes.ToArray(), logs, stateRoot); } } @@ -410,15 +410,13 @@ protected TransactionResult BuildExecutionEnvironment( in BlockExecutionContext blCtx, IReleaseSpec spec, in UInt256 effectiveGasPrice, - out ExecutionEnvironment? env) + out ExecutionEnvironment env) { Address recipient = tx.GetRecipient(tx.IsContractCreation ? WorldState.GetNonce(tx.SenderAddress) : 0); if (recipient is null) ThrowInvalidDataException("Recipient has not been resolved properly before tx execution"); TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes); - env = null; - ICodeInfo codeInfo = null; byte[] inputData = tx.IsMessageCall ? tx.Data.AsArray() ?? Array.Empty() : Array.Empty(); if (tx.IsContractCreation) @@ -427,10 +425,6 @@ protected TransactionResult BuildExecutionEnvironment( { inputData = trailingData.ToArray(); } - else - { - return "Eip 7698: Invalid CreateTx Initcode"; - } } else { @@ -481,140 +475,146 @@ protected void ExecuteEvmCall( try { - if (tx.IsContractCreation) + if (env.CodeInfo is not null) { - // if transaction is a contract creation then recipient address is the contract deployment address - PrepareAccountForContractDeployment(env.ExecutingAccount, spec); - } - - ExecutionType executionType = tx.IsContractCreation ? (tx.IsEofContractCreation ? ExecutionType.TXCREATE : ExecutionType.CREATE) : ExecutionType.TRANSACTION; - - using (EvmState state = new(unspentGas, env, executionType, true, snapshot, false)) - { - if (spec.UseTxAccessLists) + if (tx.IsContractCreation) { - state.WarmUp(tx.AccessList); // eip-2930 + // if transaction is a contract creation then recipient address is the contract deployment address + PrepareAccountForContractDeployment(env.ExecutingAccount, spec); } - if (spec.UseHotAndColdStorage) - { - state.WarmUp(tx.SenderAddress); // eip-2929 - state.WarmUp(env.ExecutingAccount); // eip-2929 - } + ExecutionType executionType = tx.IsContractCreation ? (tx.IsEofContractCreation ? ExecutionType.TXCREATE : ExecutionType.CREATE) : ExecutionType.TRANSACTION; - if (spec.AddCoinbaseToTxAccessList) - { - state.WarmUp(header.GasBeneficiary); - } - - substate = !tracer.IsTracingActions - ? VirtualMachine.Run(state, WorldState, tracer) - : VirtualMachine.Run(state, WorldState, tracer); - - unspentGas = state.GasAvailable; - - if (tracer.IsTracingAccess) - { - tracer.ReportAccess(state.AccessedAddresses, state.AccessedStorageCells); - } - } - - if (substate.ShouldRevert || substate.IsError) - { - if (Logger.IsTrace) - Logger.Trace("Restoring state from before transaction"); - WorldState.Restore(snapshot); - } - else - { - // tks: there is similar code fo contract creation from init and from CREATE - // this may lead to inconsistencies (however it is tested extensively in blockchain tests) - if (tx.IsLegacyContractCreation) + using (EvmState state = new(unspentGas, env, executionType, true, snapshot, false)) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Bytes.Length); - if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) + if (spec.UseTxAccessLists) { - ThrowOutOfGasException(); + state.WarmUp(tx.AccessList); // eip-2930 } - if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output.Bytes, 0)) + if (spec.UseHotAndColdStorage) { - ThrowInvalidCodeException(); + state.WarmUp(tx.SenderAddress); // eip-2929 + state.WarmUp(env.ExecutingAccount); // eip-2929 } - if (unspentGas >= codeDepositGasCost) + if (spec.AddCoinbaseToTxAccessList) { - var code = substate.Output.Bytes.ToArray(); - _codeInfoRepository.InsertCode(WorldState, code, env.ExecutingAccount, spec); + state.WarmUp(header.GasBeneficiary); + } - unspentGas -= codeDepositGasCost; + substate = !tracer.IsTracingActions + ? VirtualMachine.Run(state, WorldState, tracer) + : VirtualMachine.Run(state, WorldState, tracer); + + unspentGas = state.GasAvailable; + + if (tracer.IsTracingAccess) + { + tracer.ReportAccess(state.AccessedAddresses, state.AccessedStorageCells); } } - if (tx.IsEofContractCreation) + if (substate.ShouldRevert || substate.IsError) { - // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed - ReadOnlySpan auxExtraData = substate.Output.Bytes.Span; - EofCodeInfo deployCodeInfo = (EofCodeInfo)substate.Output.DeployCode; - - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, deployCodeInfo.MachineCode.Length + auxExtraData.Length); - if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) + if (Logger.IsTrace) + Logger.Trace("Restoring state from before transaction"); + WorldState.Restore(snapshot); + } + else + { + // tks: there is similar code fo contract creation from init and from CREATE + // this may lead to inconsistencies (however it is tested extensively in blockchain tests) + if (tx.IsLegacyContractCreation) { - ThrowOutOfGasException(); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Bytes.Length); + if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) + { + ThrowOutOfGasException(); + } + + if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output.Bytes, 0)) + { + ThrowInvalidCodeException(); + } + + if (unspentGas >= codeDepositGasCost) + { + var code = substate.Output.Bytes.ToArray(); + _codeInfoRepository.InsertCode(WorldState, code, env.ExecutingAccount, spec); + + unspentGas -= codeDepositGasCost; + } } - byte[] bytecodeResultArray = null; - - // 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.MachineCode.Length + auxExtraData.Length]; - // 2 - 1 - 1 - copy old container - deployCodeInfo.MachineCode.Span.CopyTo(bytecodeResult); - // 2 - 1 - 2 - copy aux data to dataSection - auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.Length..]); - - // 2 - 2 - update data section size in the header u16 - int dataSubheaderSectionStart = - EvmObjectFormat.VERSION_OFFSET // magic + version - + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) - + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) - + (deployCodeInfo.Header.ContainerSections is null - ? 0 // container section : (0 bytes if no container section is available) - : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) - + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator - - ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); - bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); - bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); - - bytecodeResultArray = bytecodeResult.ToArray(); - - // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); - if (unspentGas >= codeDepositGasCost && !invalidCode) + if (tx.IsEofContractCreation) { - // 4 - set state[new_address].code to the updated deploy container - // push new_address onto the stack (already done before the ifs) - _codeInfoRepository.InsertCode(WorldState, bytecodeResultArray, env.ExecutingAccount, spec); - unspentGas -= codeDepositGasCost; + // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed + ReadOnlySpan auxExtraData = substate.Output.Bytes.Span; + EofCodeInfo deployCodeInfo = (EofCodeInfo)substate.Output.DeployCode; + + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, deployCodeInfo.MachineCode.Length + auxExtraData.Length); + if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) + { + ThrowOutOfGasException(); + } + + byte[] bytecodeResultArray = null; + + // 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.MachineCode.Length + auxExtraData.Length]; + // 2 - 1 - 1 - copy old container + deployCodeInfo.MachineCode.Span.CopyTo(bytecodeResult); + // 2 - 1 - 2 - copy aux data to dataSection + auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.Length..]); + + // 2 - 2 - update data section size in the header u16 + int dataSubheaderSectionStart = + EvmObjectFormat.VERSION_OFFSET // magic + version + + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) + + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) + + (deployCodeInfo.Header.ContainerSections is null + ? 0 // container section : (0 bytes if no container section is available) + : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator + + ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); + bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); + bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); + + bytecodeResultArray = bytecodeResult.ToArray(); + + // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts + bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); + if (unspentGas >= codeDepositGasCost && !invalidCode) + { + // 4 - set state[new_address].code to the updated deploy container + // push new_address onto the stack (already done before the ifs) + _codeInfoRepository.InsertCode(WorldState, bytecodeResultArray, env.ExecutingAccount, spec); + unspentGas -= codeDepositGasCost; + } } - } - foreach (Address toBeDestroyed in substate.DestroyList) - { - if (Logger.IsTrace) - Logger.Trace($"Destroying account {toBeDestroyed}"); + foreach (Address toBeDestroyed in substate.DestroyList) + { + if (Logger.IsTrace) + Logger.Trace($"Destroying account {toBeDestroyed}"); - WorldState.ClearStorage(toBeDestroyed); - WorldState.DeleteAccount(toBeDestroyed); + WorldState.ClearStorage(toBeDestroyed); + WorldState.DeleteAccount(toBeDestroyed); - if (tracer.IsTracingRefunds) - tracer.ReportRefund(RefundOf.Destroy(spec.IsEip3529Enabled)); + if (tracer.IsTracingRefunds) + tracer.ReportRefund(RefundOf.Destroy(spec.IsEip3529Enabled)); + } } - - statusCode = StatusCode.Success; } - + else + { + substate = TransactionSubstate.FailedInitCode; + } spentGas = Refund(tx, header, spec, opts, substate, unspentGas, env.TxExecutionContext.GasPrice); + + statusCode = env.CodeInfo is not null ? StatusCode.Success : StatusCode.Failure; } catch (Exception ex) when (ex is EvmException or OverflowException) // TODO: OverflowException? still needed? hope not { diff --git a/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs b/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs index c5d6415a4be..e7c11793550 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs @@ -62,6 +62,17 @@ public TransactionSubstate(EvmExceptionType exceptionType, bool isTracerConnecte ShouldRevert = false; } + public static readonly TransactionSubstate FailedInitCode = new TransactionSubstate(); + + private TransactionSubstate() + { + Error = "Eip 7698: Invalid CreateTx InitCode"; + Refund = 0; + DestroyList = _emptyDestroyList; + Logs = _emptyLogs; + ShouldRevert = true; + } + public TransactionSubstate((ICodeInfo eofDeployCode, ReadOnlyMemory bytes) output, long refund, IReadOnlyCollection
destroyList, diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 81cd907963b..f4c2e971cd6 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -9,6 +9,7 @@ using Nethermind.Core.Caching; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; using Nethermind.Core.Resettables; using Nethermind.Core.Specs; using Nethermind.Int256; @@ -213,7 +214,7 @@ Account GetThroughCacheCheckExists() UInt256 newBalance = isSubtracting ? account.Balance - balanceChange : account.Balance + balanceChange; Account changedAccount = account.WithChangedBalance(newBalance); - if (_logger.IsTrace) _logger.Trace($" Update {address} B {account.Balance} -> {newBalance} ({(isSubtracting ? "-" : "+")}{balanceChange})"); + if (_logger.IsTrace) _logger.Trace($" Update {address} B {account.Balance.ToHexString(skipLeadingZeros: true)} -> {newBalance.ToHexString(skipLeadingZeros: true)} ({(isSubtracting ? "-" : "+")}{balanceChange})"); PushUpdate(address, changedAccount); } @@ -262,7 +263,7 @@ public void IncrementNonce(Address address, UInt256 delta) } Account changedAccount = account.WithChangedNonce(account.Nonce + delta); - if (_logger.IsTrace) _logger.Trace($" Update {address} N {account.Nonce} -> {changedAccount.Nonce}"); + if (_logger.IsTrace) _logger.Trace($" Update {address} N {account.Nonce.ToHexString(skipLeadingZeros: true)} -> {changedAccount.Nonce.ToHexString(skipLeadingZeros: true)}"); PushUpdate(address, changedAccount); } @@ -276,7 +277,7 @@ public void DecrementNonce(Address address, UInt256 delta) } Account changedAccount = account.WithChangedNonce(account.Nonce - delta); - if (_logger.IsTrace) _logger.Trace($" Update {address} N {account.Nonce} -> {changedAccount.Nonce}"); + if (_logger.IsTrace) _logger.Trace($" Update {address} N {account.Nonce.ToHexString(skipLeadingZeros: true)} -> {changedAccount.Nonce.ToHexString(skipLeadingZeros: true)}"); PushUpdate(address, changedAccount); } @@ -381,7 +382,7 @@ public void Restore(int snapshot) public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) { _needsStateRootUpdate = true; - if (_logger.IsTrace) _logger.Trace($"Creating account: {address} with balance {balance} and nonce {nonce}"); + if (_logger.IsTrace) _logger.Trace($"Creating account: {address} with balance {balance.ToHexString(skipLeadingZeros: true)} and nonce {nonce.ToHexString(skipLeadingZeros: true)}"); Account account = (balance.IsZero && nonce.IsZero) ? Account.TotallyEmpty : new Account(nonce, balance); PushNew(address, account); } @@ -500,7 +501,7 @@ public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer stateTracer, bool { if (releaseSpec.IsEip158Enabled && change.Account.IsEmpty && !isGenesis) { - if (_logger.IsTrace) _logger.Trace($" Commit remove empty {change.Address} B = {change.Account.Balance} N = {change.Account.Nonce}"); + if (_logger.IsTrace) _logger.Trace($" Commit remove empty {change.Address} B = {change.Account.Balance.ToHexString(skipLeadingZeros: true)} N = {change.Account.Nonce.ToHexString(skipLeadingZeros: true)}"); SetState(change.Address, null); if (isTracing) { @@ -509,7 +510,7 @@ public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer stateTracer, bool } else { - if (_logger.IsTrace) _logger.Trace($" Commit update {change.Address} B = {change.Account.Balance} N = {change.Account.Nonce} C = {change.Account.CodeHash}"); + if (_logger.IsTrace) _logger.Trace($" Commit update {change.Address} B = {change.Account.Balance.ToHexString(skipLeadingZeros: true)} N = {change.Account.Nonce.ToHexString(skipLeadingZeros: true)} C = {change.Account.CodeHash}"); SetState(change.Address, change.Account); if (isTracing) { @@ -523,7 +524,7 @@ public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer stateTracer, bool { if (!releaseSpec.IsEip158Enabled || !change.Account.IsEmpty || isGenesis) { - if (_logger.IsTrace) _logger.Trace($" Commit create {change.Address} B = {change.Account.Balance} N = {change.Account.Nonce}"); + if (_logger.IsTrace) _logger.Trace($" Commit create {change.Address} B = {change.Account.Balance.ToHexString(skipLeadingZeros: true)} N = {change.Account.Nonce.ToHexString(skipLeadingZeros: true)}"); SetState(change.Address, change.Account); if (isTracing) { diff --git a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs index 3ab8c9d1127..1fa74065476 100644 --- a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs +++ b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs @@ -517,7 +517,7 @@ public virtual void Set(ReadOnlySpan rawKey, in CappedArray value) void Trace(in ReadOnlySpan rawKey, in CappedArray value) { - _logger.Trace($"{(value.Length == 0 ? $"Deleting {rawKey.ToHexString()}" : $"Setting {rawKey.ToHexString()} = {value.AsSpan().ToHexString()}")}"); + _logger.Trace($"{(value.Length == 0 ? $"Deleting {rawKey.ToHexString(withZeroX: true)}" : $"Setting {rawKey.ToHexString(withZeroX: true)} = {value.AsSpan().ToHexString(withZeroX: true)}")}"); } [DoesNotReturn] From 584cc43ed0d722bb1168d0881c7c24f74e6d694e Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 18 Jul 2024 06:25:46 +0100 Subject: [PATCH 124/255] Fix Evm tests --- .../TransactionProcessing/TransactionProcessor.cs | 4 ++-- src/Nethermind/Nethermind.Evm/TransactionSubstate.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 6fbbe3dcd04..e76ecce231e 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -606,6 +606,8 @@ protected void ExecuteEvmCall( if (tracer.IsTracingRefunds) tracer.ReportRefund(RefundOf.Destroy(spec.IsEip3529Enabled)); } + + statusCode = StatusCode.Success; } } else @@ -613,8 +615,6 @@ protected void ExecuteEvmCall( substate = TransactionSubstate.FailedInitCode; } spentGas = Refund(tx, header, spec, opts, substate, unspentGas, env.TxExecutionContext.GasPrice); - - statusCode = env.CodeInfo is not null ? StatusCode.Success : StatusCode.Failure; } catch (Exception ex) when (ex is EvmException or OverflowException) // TODO: OverflowException? still needed? hope not { diff --git a/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs b/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs index e7c11793550..a54777f6d70 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionSubstate.cs @@ -62,7 +62,7 @@ public TransactionSubstate(EvmExceptionType exceptionType, bool isTracerConnecte ShouldRevert = false; } - public static readonly TransactionSubstate FailedInitCode = new TransactionSubstate(); + public static TransactionSubstate FailedInitCode { get; } = new TransactionSubstate(); private TransactionSubstate() { From 49f3b8e9c058ea51b8b42878013842a2288d8173 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 18 Jul 2024 07:17:51 +0100 Subject: [PATCH 125/255] Update current state for RETURNCONTRACT --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8f45a0bb9bf..419c45b90b9 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2314,6 +2314,7 @@ private CallResult ExecuteCode Date: Thu, 18 Jul 2024 08:23:01 +0100 Subject: [PATCH 126/255] Mark test as incorrect --- src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 400b88059fe..f783f4d18d5 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -168,6 +168,7 @@ protected override ILogManager GetLogManager() return _logManager; } + [Ignore("Test Incorrect")] [TestCase(0)] [TestCase(MainnetSpecProvider.HomesteadBlockNumber)] [TestCase(MainnetSpecProvider.SpuriousDragonBlockNumber)] From ece6ba8526251420f1203f3ca326a8759e77bf23 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 18 Jul 2024 08:44:48 +0100 Subject: [PATCH 127/255] Tidy up usings --- src/Nethermind/Ethereum.Basic.Test/TransactionTests.cs | 1 - .../Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs | 1 - src/Nethermind/Ethereum.Blockchain.Test/BadOpcodeTests.cs | 1 - src/Nethermind/Ethereum.Test.Base/EthereumTestResult.cs | 2 -- .../Ethereum.Test.Base/LoadBlockchainTestFileStrategy.cs | 1 - .../Nethermind.Consensus/Validators/HeaderValidator.cs | 1 - src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs | 2 -- .../Nethermind.Evm.Test/CodeAnalysis/CodeInfoTests.cs | 1 - src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 1 - .../Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs | 1 - src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs | 3 --- src/Nethermind/Nethermind.Evm/EvmState.cs | 1 - .../TransactionProcessing/TransactionProcessor.cs | 1 - src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 1 - src/Nethermind/Nethermind.State/IWorldStateExtensions.cs | 2 -- src/Nethermind/Nethermind.State/PersistentStorageProvider.cs | 1 - src/Nethermind/Nethermind.State/StateReader.cs | 2 -- src/Nethermind/Nethermind.State/WorldState.cs | 1 - 18 files changed, 24 deletions(-) diff --git a/src/Nethermind/Ethereum.Basic.Test/TransactionTests.cs b/src/Nethermind/Ethereum.Basic.Test/TransactionTests.cs index 1ce9cf6f735..7d919901031 100644 --- a/src/Nethermind/Ethereum.Basic.Test/TransactionTests.cs +++ b/src/Nethermind/Ethereum.Basic.Test/TransactionTests.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Numerics; -using System.Text.Json.Serialization; using Ethereum.Test.Base; using Nethermind.Core; diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs index e0db71c5c14..ba6b47c5200 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading.Tasks; using Ethereum.Test.Base; -using FluentAssertions; using NUnit.Framework; namespace Ethereum.Blockchain.Pyspec.Test; diff --git a/src/Nethermind/Ethereum.Blockchain.Test/BadOpcodeTests.cs b/src/Nethermind/Ethereum.Blockchain.Test/BadOpcodeTests.cs index e90821b0a4a..df833c3fa52 100644 --- a/src/Nethermind/Ethereum.Blockchain.Test/BadOpcodeTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Test/BadOpcodeTests.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; -using System.Linq; using Ethereum.Test.Base; using NUnit.Framework; diff --git a/src/Nethermind/Ethereum.Test.Base/EthereumTestResult.cs b/src/Nethermind/Ethereum.Test.Base/EthereumTestResult.cs index eecd49c13e9..0cb2f1adf3c 100644 --- a/src/Nethermind/Ethereum.Test.Base/EthereumTestResult.cs +++ b/src/Nethermind/Ethereum.Test.Base/EthereumTestResult.cs @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Text.Json.Serialization; - using Nethermind.Core.Crypto; namespace Ethereum.Test.Base diff --git a/src/Nethermind/Ethereum.Test.Base/LoadBlockchainTestFileStrategy.cs b/src/Nethermind/Ethereum.Test.Base/LoadBlockchainTestFileStrategy.cs index e629f80b277..c8a2f6c0d7b 100644 --- a/src/Nethermind/Ethereum.Test.Base/LoadBlockchainTestFileStrategy.cs +++ b/src/Nethermind/Ethereum.Test.Base/LoadBlockchainTestFileStrategy.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using Ethereum.Test.Base.Interfaces; namespace Ethereum.Test.Base diff --git a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs index 368524c6bb1..e360137a63b 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using Microsoft.Extensions.Options; using Nethermind.Blockchain; using Nethermind.Blockchain.Find; using Nethermind.Consensus.Messages; diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index bf37e38d874..ebb4c1da379 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -5,11 +5,9 @@ using Nethermind.Consensus.Messages; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Evm; -using Nethermind.Evm.EOF; using Nethermind.Int256; using Nethermind.TxPool; diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/CodeInfoTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/CodeInfoTests.cs index 67490a397e1..220af1f5d0a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/CodeInfoTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/CodeInfoTests.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Linq; -using System.Reflection; using System.Runtime.Intrinsics; using FluentAssertions; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 509118577d0..9a4736d707c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -4,7 +4,6 @@ using System; using System.Threading; -using System.Runtime.CompilerServices; using Nethermind.Evm.EOF; using Nethermind.Evm.Precompiles; using System.Diagnostics; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index dc4d06c5d99..c8b52b3add8 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using DotNetty.Common.Utilities; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index b2c2c4f885c..e422c28ac82 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -2,10 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Drawing; using System.Linq; -using Nethermind.Core.Extensions; -using Nethermind.Evm.CodeAnalysis; namespace Nethermind.Evm.EOF; diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index 400fc2fd23c..696456f547b 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Threading; diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index e76ecce231e..2bc1d757d8f 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -19,7 +19,6 @@ using Nethermind.Specs; using Nethermind.State; using Nethermind.State.Tracing; -using Org.BouncyCastle.Bcpg; using static Nethermind.Core.Extensions.MemoryExtensions; using static Nethermind.Evm.VirtualMachine; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 419c45b90b9..2ef93deaeeb 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; -using System.Security.Cryptography; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; diff --git a/src/Nethermind/Nethermind.State/IWorldStateExtensions.cs b/src/Nethermind/Nethermind.State/IWorldStateExtensions.cs index f6ffa4250d1..d62e8241df7 100644 --- a/src/Nethermind/Nethermind.State/IWorldStateExtensions.cs +++ b/src/Nethermind/Nethermind.State/IWorldStateExtensions.cs @@ -4,9 +4,7 @@ using System; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Specs; -using Nethermind.Logging; using Nethermind.Trie; namespace Nethermind.State diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index e07605a58e9..f00b7c475b1 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.InteropServices; diff --git a/src/Nethermind/Nethermind.State/StateReader.cs b/src/Nethermind/Nethermind.State/StateReader.cs index 77558d564c2..41a230e1dc6 100644 --- a/src/Nethermind/Nethermind.State/StateReader.cs +++ b/src/Nethermind/Nethermind.State/StateReader.cs @@ -2,11 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; -using Nethermind.Db; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Trie; diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 60f5492cd60..85344254a4a 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Collections; From dd347e060f4506b17ce406f957c291672df09e83 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 20 Jul 2024 01:03:48 +0100 Subject: [PATCH 128/255] Update tests to v1.0.7 --- .../Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs index ba6b47c5200..74f8d3c5a4d 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.6" + ArchiveVersion = "eip7692@v1.0.7" }, $"fixtures/blockchain_tests/prague"); return loader.LoadTests().Cast(); } diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index f2576e28b3b..2f0c06210a4 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.6" + ArchiveVersion = "eip7692@v1.0.7" }, $"fixtures/state_tests/prague"); return loader.LoadTests().Cast(); } From c31667b32c127f1f1cb309313008b10d4e6c5ba2 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 20 Jul 2024 07:08:40 +0100 Subject: [PATCH 129/255] Spelling --- .../Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs index 74f8d3c5a4d..9f76504fb07 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -11,7 +11,7 @@ namespace Ethereum.Blockchain.Pyspec.Test; [TestFixture] [Parallelizable(ParallelScope.All)] -public class PrageBlockChainTests : BlockchainTestBase +public class PragueBlockChainTests : BlockchainTestBase { [TestCaseSource(nameof(LoadTests))] public async Task Test(BlockchainTest test) => await RunTest(test); From a5010308e686a1d78900cb1d62fbfde88de32bf8 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 20 Jul 2024 09:05:51 +0100 Subject: [PATCH 130/255] Simpler if --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 2ef93deaeeb..a52ecf3a5e8 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -417,7 +417,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl bytecodeResultArray = bytecodeResult.ToArray(); // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - bool invalidCode = !(bytecodeResultArray.Length <= spec.MaxCodeSize); + bool invalidCode = bytecodeResultArray.Length > spec.MaxCodeSize; long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, bytecodeResultArray?.Length ?? 0); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { From b5d5dd68afb55495975420c566312c60998b1a6a Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 15 Aug 2024 14:03:40 +0100 Subject: [PATCH 131/255] Update tests --- .../Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs index 9f76504fb07..904c442a414 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.7" + ArchiveVersion = "eip7692@v1.0.8" }, $"fixtures/blockchain_tests/prague"); return loader.LoadTests().Cast(); } diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index 2f0c06210a4..2a8996eaae9 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.7" + ArchiveVersion = "eip7692@v1.0.8" }, $"fixtures/state_tests/prague"); return loader.LoadTests().Cast(); } From b48ed47b3748b6ee841660c26e5a0cf4bdbca8e7 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 20 Aug 2024 13:25:23 +0100 Subject: [PATCH 132/255] add missing opcodes from metadata dictionary --- src/Nethermind/Nethermind.Evm/Instruction.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 1cf7ff09878..de5fabb20dd 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -341,6 +341,16 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.REVERT => (2, 0, 0), Instruction.INVALID => (0, 0, 0), + Instruction.TLOAD => (1, 1, 0), + Instruction.TSTORE => (2, 0, 0), + Instruction.MCOPY => (3, 0, 0), + Instruction.CALLCODE => (6, 1, 0), + Instruction.SELFDESTRUCT => (1, 0, 0), + Instruction.JUMP => (1, 0, 0), + Instruction.JUMPI => (2, 0, 0), + Instruction.PC => (0, 1, 0), + Instruction.BLOBBASEFEE => (0, 1, 0), + Instruction.EOFCREATE => (4, 1, 1), Instruction.RETURNCONTRACT => (2, 2, 1), Instruction.DATALOAD => (1, 1, 0), From b65bab4f47f3a95a9752fdb99d708ce51b1ea608 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 26 Aug 2024 11:37:27 +0100 Subject: [PATCH 133/255] * refactor nested containers validations * refactor file structure --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 +- .../Nethermind.Evm/CodeDepositHandler.cs | 14 +- .../Nethermind.Evm/CodeInfoFactory.cs | 16 +- .../Nethermind.Evm/CodeInfoRepository.cs | 7 +- .../EvmObjectFormat/EofCodeInfo.cs | 48 +- .../EvmObjectFormat/EofCodeValidator.cs | 1156 +---------------- .../EvmObjectFormat/EofHeader.cs | 60 +- .../EvmObjectFormat/EofValidationStrategy.cs | 22 + .../EvmObjectFormat/Handlers/EofV1.cs | 1023 +++++++++++++++ .../EvmObjectFormat/IEofVersionHandler.cs | 12 + src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 2 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 6 +- .../ReadOnlyMemoryExtensions.cs | 10 + .../TransactionProcessor.cs | 16 +- .../Nethermind.Evm/VirtualMachine.cs | 66 +- 15 files changed, 1233 insertions(+), 1227 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs create mode 100644 src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs create mode 100644 src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 9a4736d707c..5cf63dcc7aa 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -4,9 +4,9 @@ using System; using System.Threading; -using Nethermind.Evm.EOF; using Nethermind.Evm.Precompiles; using System.Diagnostics; +using Nethermind.Evm.EvmObjectFormat; namespace Nethermind.Evm.CodeAnalysis { diff --git a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs index 5c624d07b20..44d507d7690 100644 --- a/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeDepositHandler.cs @@ -3,7 +3,7 @@ using System; using Nethermind.Core.Specs; -using Nethermind.Evm.EOF; +using Nethermind.Evm.EvmObjectFormat; namespace Nethermind.Evm { @@ -22,18 +22,18 @@ public static bool CodeIsInvalid(IReleaseSpec spec, ReadOnlyMemory code, i => !CodeIsValid(spec, code, fromVersion); public static bool CodeIsValid(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion) - => spec.IsEofEnabled ? IsValidWithEofRules(spec, code.Span, fromVersion) : IsValidWithLegacyRules(spec, code.Span); + => spec.IsEofEnabled ? IsValidWithEofRules(spec, code, fromVersion) : IsValidWithLegacyRules(spec, code); - public static bool IsValidWithLegacyRules(IReleaseSpec spec, ReadOnlySpan code) - => !spec.IsEip3541Enabled || code is not [InvalidStartingCodeByte, ..]; + public static bool IsValidWithLegacyRules(IReleaseSpec spec, ReadOnlyMemory code) + => !spec.IsEip3541Enabled || !code.StartsWith(InvalidStartingCodeByte); - public static bool IsValidWithEofRules(IReleaseSpec spec, ReadOnlySpan code, int fromVersion, EvmObjectFormat.ValidationStrategy strategy = EvmObjectFormat.ValidationStrategy.Validate) + public static bool IsValidWithEofRules(IReleaseSpec spec, ReadOnlyMemory code, int fromVersion, EvmObjectFormat.ValidationStrategy strategy = EvmObjectFormat.ValidationStrategy.Validate) { - bool isCodeEof = EvmObjectFormat.IsEof(code, out int codeVersion); + bool isCodeEof = EofValidator.IsEof(code, out byte codeVersion); bool valid = code.Length >= 1 && codeVersion >= fromVersion && (isCodeEof - ? EvmObjectFormat.IsValidEof(code, strategy, out _) + ? EofValidator.IsValidEof(code, strategy, out _) : (fromVersion > 0 ? false : IsValidWithLegacyRules(spec, code))); return valid; } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index 77e74f670f3..aaa8d0066ca 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core.Specs; -using Nethermind.Evm.EOF; +using Nethermind.Evm.EvmObjectFormat; using System; namespace Nethermind.Evm.CodeAnalysis; @@ -12,11 +12,11 @@ public static class CodeInfoFactory public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, EvmObjectFormat.ValidationStrategy validationRules = EvmObjectFormat.ValidationStrategy.ExractHeader) { CodeInfo codeInfo = new CodeInfo(code); - if (spec.IsEofEnabled && code.Span.StartsWith(EvmObjectFormat.MAGIC)) + if (spec.IsEofEnabled && code.Span.StartsWith(EofValidator.MAGIC)) { - if (EvmObjectFormat.IsValidEof(code.Span, validationRules, out EofHeader? header)) + if (EofValidator.IsValidEof(code, validationRules, out EofContainer? container)) { - return new EofCodeInfo(codeInfo, header.Value); + return new EofCodeInfo(container.Value); } } codeInfo.AnalyseInBackgroundIfRequired(); @@ -26,14 +26,14 @@ public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec s public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out ICodeInfo codeInfo, out Memory extraCalldata) { extraCalldata = default; - if (spec.IsEofEnabled && data.Span.StartsWith(EvmObjectFormat.MAGIC)) + if (spec.IsEofEnabled && data.Span.StartsWith(EofValidator.MAGIC)) { - if (EvmObjectFormat.IsValidEof(data.Span, EvmObjectFormat.ValidationStrategy.ValidateInitcodeMode | EvmObjectFormat.ValidationStrategy.ValidateFullBody | EvmObjectFormat.ValidationStrategy.AllowTrailingBytes, out EofHeader? header)) + if (EofValidator.IsValidEof(data, EvmObjectFormat.ValidationStrategy.ValidateInitcodeMode | EvmObjectFormat.ValidationStrategy.ValidateFullBody | EvmObjectFormat.ValidationStrategy.AllowTrailingBytes, out EofContainer? eofContainer)) { - int containerSize = header.Value.DataSection.EndOffset; + int containerSize = eofContainer.Value.Header.DataSection.EndOffset; extraCalldata = data.Slice(containerSize); ICodeInfo innerCodeInfo = new CodeInfo(data.Slice(0, containerSize)); - codeInfo = new EofCodeInfo(innerCodeInfo, header.Value); + codeInfo = new EofCodeInfo(eofContainer.Value); return true; } codeInfo = null; diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 49a508b52fa..3de49539db6 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -16,6 +16,7 @@ using Nethermind.Evm.Precompiles.Bls; using Nethermind.Evm.Precompiles.Snarks; using Nethermind.State; +using Nethermind.Evm.EvmObjectFormat; namespace Nethermind.Evm; @@ -126,7 +127,7 @@ public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, I MissingCode(codeSource, codeHash); } - cachedCodeInfo = CodeInfoFactory.CreateCodeInfo(code, vmSpec, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); + cachedCodeInfo = CodeInfoFactory.CreateCodeInfo(code, vmSpec, Nethermind.Evm.EvmObjectFormat.ValidationStrategy.ExractHeader); _codeCache.Set(codeHash, cachedCodeInfo); } else @@ -148,7 +149,7 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR { if (!_codeCache.TryGet(codeHash, out ICodeInfo? codeInfo)) { - codeInfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); + codeInfo = CodeInfoFactory.CreateCodeInfo(initCode.ToArray(), spec, ValidationStrategy.ExractHeader); // Prime the code cache as likely to be used by more txs _codeCache.Set(codeHash, codeInfo); @@ -160,7 +161,7 @@ public ICodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode, IR public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec, EOF.EvmObjectFormat.ValidationStrategy.ExractHeader); + ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec, ValidationStrategy.ExractHeader); Hash256 codeHash = code.Length == 0 ? Keccak.OfAnEmptyString : Keccak.Compute(code.Span); state.InsertCode(codeOwner, codeHash, code, spec); _codeCache.Set(codeHash, codeInfo); diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index 0bbc905e669..296a1bc101d 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -3,49 +3,39 @@ using System; using Nethermind.Core.Extensions; -using Nethermind.Evm.EOF; +using Nethermind.Evm.EvmObjectFormat; +using Nethermind.Evm.EvmObjectFormat.Handlers; using Nethermind.Evm.Precompiles; namespace Nethermind.Evm.CodeAnalysis; public class EofCodeInfo : ICodeInfo { - private readonly ICodeInfo _codeInfo; + public EofContainer EofContainer { get; private set; } + public ReadOnlyMemory MachineCode => EofContainer.Container; + public IPrecompile? Precompile => null; + public int Version => EofContainer.Header.Version; + public bool IsEmpty => EofContainer.IsEmpty; + public ReadOnlyMemory TypeSection => EofContainer.TypeSection; + public ReadOnlyMemory CodeSection => EofContainer.CodeSection; + public ReadOnlyMemory DataSection => EofContainer.DataSection; + public ReadOnlyMemory ContainerSection => EofContainer.ContainerSection; - public EofHeader Header { get; private set; } - public ReadOnlyMemory MachineCode => _codeInfo.MachineCode; - public IPrecompile? Precompile => _codeInfo.Precompile; - public int Version => Header.Version; - public bool IsEmpty => _codeInfo.IsEmpty; - public ReadOnlyMemory TypeSection { get; } - public ReadOnlyMemory CodeSection { get; } - public ReadOnlyMemory DataSection { get; } - public ReadOnlyMemory ContainerSection { get; } - - public SectionHeader CodeSectionOffset(int sectionId) => Header.CodeSections[sectionId]; - public SectionHeader? ContainerSectionOffset(int containerId) => - Header.ContainerSections is null - ? null - : Header.ContainerSections.Value[containerId]; + public SectionHeader CodeSectionOffset(int sectionId) => EofContainer.Header.CodeSections[sectionId]; + public SectionHeader? ContainerSectionOffset(int sectionId) => EofContainer.Header.ContainerSections.Value[sectionId]; public (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) { - ReadOnlySpan typesectionSpan = TypeSection.Span; - int TypeSectionSectionOffset = index * EvmObjectFormat.Eof1.MINIMUM_TYPESECTION_SIZE; + ReadOnlySpan typesectionSpan = EofContainer.TypeSections[index].Span; return ( - typesectionSpan[TypeSectionSectionOffset + EvmObjectFormat.Eof1.INPUTS_OFFSET], - typesectionSpan[TypeSectionSectionOffset + EvmObjectFormat.Eof1.OUTPUTS_OFFSET], - typesectionSpan.Slice(TypeSectionSectionOffset + EvmObjectFormat.Eof1.MAX_STACK_HEIGHT_OFFSET, EvmObjectFormat.Eof1.MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16() + typesectionSpan[Eof1.INPUTS_OFFSET], + typesectionSpan[Eof1.OUTPUTS_OFFSET], + typesectionSpan.Slice(Eof1.MAX_STACK_HEIGHT_OFFSET, Eof1.MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16() ); } - public EofCodeInfo(ICodeInfo codeInfo, in EofHeader header) + public EofCodeInfo(in EofContainer container) { - _codeInfo = codeInfo; - Header = header; - TypeSection = MachineCode.Slice(Header.TypeSection.Start, Header.TypeSection.Size); - CodeSection = MachineCode.Slice(Header.CodeSections.Start, Header.CodeSections.Size); - DataSection = MachineCode.Slice(Header.DataSection.Start); - ContainerSection = Header.ContainerSections is null ? Memory.Empty : MachineCode.Slice(Header.ContainerSections.Value.Start, Header.ContainerSections.Value.Size); + EofContainer = container; } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index c8b52b3add8..b26a9a72884 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; @@ -11,65 +12,16 @@ using DotNetty.Common.Utilities; using FastEnumUtility; using Nethermind.Core.Extensions; +using Nethermind.Evm.EvmObjectFormat; +using Nethermind.Evm.EvmObjectFormat.Handlers; using Nethermind.Logging; [assembly: InternalsVisibleTo("Nethermind.EofParser")] -namespace Nethermind.Evm.EOF; +namespace Nethermind.Evm.EvmObjectFormat; -public static class EvmObjectFormat +public static class EofValidator { - [StructLayout(LayoutKind.Sequential)] - struct Worklet - { - public Worklet(ushort position, StackBounds stackHeightBounds) - { - Position = position; - StackHeightBounds = stackHeightBounds; - } - public ushort Position; - public StackBounds StackHeightBounds; - } - - [StructLayout(LayoutKind.Sequential)] - struct StackBounds() - { - public short Max = -1; - public short Min = 1023; - - public void Combine(StackBounds other) - { - this.Max = Math.Max(this.Max, other.Max); - this.Min = Math.Min(this.Min, other.Min); - } - - public bool BoundsEqual() => Max == Min; - - public static bool operator ==(StackBounds left, StackBounds right) => left.Max == right.Max && right.Min == left.Min; - public static bool operator !=(StackBounds left, StackBounds right) => !(left == right); - public override bool Equals(object obj) => obj is StackBounds && this == (StackBounds)obj; - public override int GetHashCode() => Max ^ Min; - } - - public enum ValidationStrategy - { - None = 0, - Validate = 1, - ValidateFullBody = Validate | 2, - ValidateInitcodeMode = Validate | 4, - ValidateRuntimeMode = Validate | 8, - AllowTrailingBytes = Validate | 16, - ExractHeader = 32, - HasEofMagic = 64, - - } - - private interface IEofVersionHandler - { - bool ValidateBody(ReadOnlySpan code, EofHeader header, ValidationStrategy strategy); - bool TryParseEofHeader(ReadOnlySpan code, [NotNullWhen(true)] out EofHeader? header); - } - // magic prefix : EofFormatByte is the first byte, EofFormatDiff is chosen to diff from previously rejected contract according to EIP3541 public static byte[] MAGIC = { 0xEF, 0x00 }; public const byte ONE_BYTE_LENGTH = 1; @@ -79,7 +31,7 @@ private interface IEofVersionHandler private static readonly Dictionary _eofVersionHandlers = new(); internal static ILogger Logger { get; set; } = NullLogger.Instance; - static EvmObjectFormat() + static EofValidator() { _eofVersionHandlers.Add(Eof1.VERSION, new Eof1()); } @@ -89,11 +41,11 @@ static EvmObjectFormat() ///
/// Machine code to be checked /// - public static bool IsEof(ReadOnlySpan container, [NotNullWhen(true)] out int version) + public static bool IsEof(ReadOnlyMemory container, [NotNullWhen(true)] out byte version) { if (container.Length >= MAGIC.Length + 1) { - version = container[MAGIC.Length]; + version = container.ByteAt(MAGIC.Length); return container.StartsWith(MAGIC); } else @@ -104,1103 +56,37 @@ public static bool IsEof(ReadOnlySpan container, [NotNullWhen(true)] out i } - public static bool IsValidEof(ReadOnlySpan container, ValidationStrategy strategy, [NotNullWhen(true)] out EofHeader? header) + public static bool IsValidEofHeader(ReadOnlyMemory code, [NotNullWhen(true)] out EofHeader? header) { - if (strategy == ValidationStrategy.None) + if (IsEof(code, out byte version) && _eofVersionHandlers.TryGetValue(version, out IEofVersionHandler handler)) { - header = null; - return true; - } - - if (strategy.HasFlag(ValidationStrategy.HasEofMagic) && !container.StartsWith(MAGIC)) - { - header = null; - return false; - } - - if (container.Length > VERSION_OFFSET - && _eofVersionHandlers.TryGetValue(container[VERSION_OFFSET], out IEofVersionHandler handler) - && handler.TryParseEofHeader(container, out header)) - { - bool validateBody = strategy.HasFlag(ValidationStrategy.Validate); - if (validateBody && handler.ValidateBody(container, header.Value, strategy)) - { - return true; - } - return !validateBody; + return handler.TryParseEofHeader(code, out header); } header = null; return false; } - internal class Eof1 : IEofVersionHandler + public static bool IsValidEof(ReadOnlyMemory code, ValidationStrategy strategy, [NotNullWhen(true)] out EofContainer? eofContainer) { - private ref struct Sizes - { - public ushort? TypeSectionSize; - public ushort? CodeSectionSize; - public ushort? DataSectionSize; - public ushort? ContainerSectionSize; - } - - public const byte VERSION = 0x01; - internal enum Separator : byte - { - KIND_TYPE = 0x01, - KIND_CODE = 0x02, - KIND_CONTAINER = 0x03, - KIND_DATA = 0x04, - TERMINATOR = 0x00 - } - - 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 ushort MAXIMUM_NUM_CONTAINER_SECTIONS = 0x00FF; - internal const ushort RETURN_STACK_MAX_HEIGHT = MAXIMUM_NUM_CODE_SECTIONS; // the size in the type sectionn 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 - - public bool TryParseEofHeader(ReadOnlySpan container, out EofHeader? header) + if (strategy == ValidationStrategy.None) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static ushort GetUInt16(ReadOnlySpan container, int offset) => - container.Slice(offset, TWO_BYTE_LENGTH).ReadEthUInt16(); - - header = null; - // we need to be able to parse header + minimum section lenghts - 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.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; - } - - Sizes sectionSizes = new(); - int[] codeSections = null; - int[] containerSections = null; - int pos = VERSION_OFFSET + 1; - - bool continueParsing = true; - while (continueParsing && pos < container.Length) - { - Separator separator = (Separator)container[pos++]; - - switch (separator) - { - case Separator.KIND_TYPE: - 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; - } - - sectionSizes.TypeSectionSize = GetUInt16(container, pos); - if (sectionSizes.TypeSectionSize < MINIMUM_TYPESECTION_SIZE) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); - return false; - } - - pos += TWO_BYTE_LENGTH; - break; - case Separator.KIND_CODE: - if (sectionSizes.TypeSectionSize is null) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); - return false; - } - - 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(container, pos); - sectionSizes.CodeSectionSize = (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; - } - - if (container.Length < pos + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * numberOfCodeSections) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - - codeSections = new int[numberOfCodeSections]; - int CODESECTION_HEADER_PREFIX_SIZE = pos + TWO_BYTE_LENGTH; - for (ushort i = 0; i < numberOfCodeSections; i++) - { - int currentCodeSizeOffset = CODESECTION_HEADER_PREFIX_SIZE + i * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size - int codeSectionSize = GetUInt16(container, currentCodeSizeOffset); - - 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 + TWO_BYTE_LENGTH * numberOfCodeSections; - break; - case Separator.KIND_CONTAINER: - if (sectionSizes.CodeSectionSize is null) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); - return false; - } - - 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(container, pos); - sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * TWO_BYTE_LENGTH); - if (numberOfContainerSections is > MAXIMUM_NUM_CONTAINER_SECTIONS or 0) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); - return false; - } - - if (container.Length < pos + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * numberOfContainerSections) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - - containerSections = new int[numberOfContainerSections]; - int CONTAINER_SECTION_HEADER_PREFIX_SIZE = pos + TWO_BYTE_LENGTH; - for (ushort i = 0; i < numberOfContainerSections; i++) - { - int currentContainerSizeOffset = CONTAINER_SECTION_HEADER_PREFIX_SIZE + i * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size - int containerSectionSize = GetUInt16(container, currentContainerSizeOffset); - - 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 + TWO_BYTE_LENGTH * numberOfContainerSections; - break; - case Separator.KIND_DATA: - if (sectionSizes.CodeSectionSize is null) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); - return false; - } - - 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; - } - - sectionSizes.DataSectionSize = GetUInt16(container, pos); - - pos += TWO_BYTE_LENGTH; - break; - case Separator.TERMINATOR: - 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; - } - } - - 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; - } - - SectionHeader typeSectionSubHeader = new SectionHeader(pos, sectionSizes.TypeSectionSize.Value); - CompoundSectionHeader codeSectionSubHeader = new CompoundSectionHeader(typeSectionSubHeader.EndOffset, codeSections); - CompoundSectionHeader? containerSectionSubHeader = containerSections is null ? null - : new CompoundSectionHeader(codeSectionSubHeader.EndOffset, containerSections); - SectionHeader dataSectionSubHeader = new SectionHeader(containerSectionSubHeader?.EndOffset ?? codeSectionSubHeader.EndOffset, sectionSizes.DataSectionSize.Value); - - header = new EofHeader - { - Version = VERSION, - PrefixSize = pos, - TypeSection = typeSectionSubHeader, - CodeSections = codeSectionSubHeader, - ContainerSections = containerSectionSubHeader, - DataSection = dataSectionSubHeader, - }; - + eofContainer = null; return true; } - public bool TryParseEofHeader2(ReadOnlySpan container, out EofHeader? header) - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static ushort GetUInt16(ReadOnlySpan container, int offset) => - container.Slice(offset, TWO_BYTE_LENGTH).ReadEthUInt16(); - header = null; - // we need to be able to parse header + minimum section lenghts - 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.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; - } - - Sizes sectionSizes = new(); - - int TYPESECTION_HEADER_STARTOFFSET = VERSION_OFFSET + ONE_BYTE_LENGTH; - int TYPESECTION_HEADER_ENDOFFSET = TYPESECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; - if (container[TYPESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_TYPE) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not Eof version {VERSION}"); - return false; - } - sectionSizes.TypeSectionSize = GetUInt16(container, TYPESECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); - if (sectionSizes.TypeSectionSize < MINIMUM_TYPESECTION_SIZE) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); - return false; - } - - int CODESECTION_HEADER_STARTOFFSET = TYPESECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; - if (container[CODESECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_CODE) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code header is not well formatted"); - return false; - } - - ushort numberOfCodeSections = GetUInt16(container, CODESECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); - sectionSizes.CodeSectionSize = (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[] codeSections = new int[numberOfCodeSections]; - int CODESECTION_HEADER_PREFIX_SIZE = CODESECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; - for (ushort pos = 0; pos < numberOfCodeSections; pos++) - { - int currentCodeSizeOffset = CODESECTION_HEADER_PREFIX_SIZE + pos * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size - int codeSectionSize = GetUInt16(container, currentCodeSizeOffset + ONE_BYTE_LENGTH); - - 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[pos] = codeSectionSize; - } - var CODESECTION_HEADER_ENDOFFSET = CODESECTION_HEADER_PREFIX_SIZE + numberOfCodeSections * TWO_BYTE_LENGTH; - - int CONTAINERSECTION_HEADER_STARTOFFSET = CODESECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; - int? CONTAINERSECTION_HEADER_ENDOFFSET = null; - int[] containerSections = null; - if (container[CONTAINERSECTION_HEADER_STARTOFFSET] == (byte)Separator.KIND_CONTAINER) - { - ushort numberOfContainerSections = GetUInt16(container, CONTAINERSECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); - sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * TWO_BYTE_LENGTH); - if (numberOfContainerSections is > MAXIMUM_NUM_CONTAINER_SECTIONS or 0) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); - return false; - } - - containerSections = new int[numberOfContainerSections]; - int CONTAINER_SECTION_HEADER_PREFIX_SIZE = CONTAINERSECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; - for (ushort pos = 0; pos < numberOfContainerSections; pos++) - { - int currentContainerSizeOffset = CONTAINER_SECTION_HEADER_PREFIX_SIZE + pos * EvmObjectFormat.TWO_BYTE_LENGTH; // offset of pos'th code size - int containerSectionSize = GetUInt16(container, currentContainerSizeOffset + ONE_BYTE_LENGTH); - - 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[pos] = containerSectionSize; - } - CONTAINERSECTION_HEADER_ENDOFFSET = CONTAINER_SECTION_HEADER_PREFIX_SIZE + numberOfContainerSections * TWO_BYTE_LENGTH; - } - - - int DATASECTION_HEADER_STARTOFFSET = CONTAINERSECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH ?? CONTAINERSECTION_HEADER_STARTOFFSET; - int DATASECTION_HEADER_ENDOFFSET = DATASECTION_HEADER_STARTOFFSET + TWO_BYTE_LENGTH; - if (container[DATASECTION_HEADER_STARTOFFSET] != (byte)Separator.KIND_DATA) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code header is not well formatted"); - return false; - } - sectionSizes.DataSectionSize = GetUInt16(container, DATASECTION_HEADER_STARTOFFSET + ONE_BYTE_LENGTH); - - - int HEADER_TERMINATOR_OFFSET = DATASECTION_HEADER_ENDOFFSET + ONE_BYTE_LENGTH; - if (container[HEADER_TERMINATOR_OFFSET] != (byte)Separator.TERMINATOR) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code header is not well formatted"); - return false; - } - - SectionHeader typeSectionHeader = new - ( - start: HEADER_TERMINATOR_OFFSET + ONE_BYTE_LENGTH, - size: sectionSizes.TypeSectionSize.Value - ); - - CompoundSectionHeader codeSectionHeader = new( - start: typeSectionHeader.EndOffset, - subSectionsSizes: codeSections - ); - - CompoundSectionHeader? containerSectionHeader = containerSections is null ? null - : new( - start: codeSectionHeader.EndOffset, - subSectionsSizes: containerSections - ); - - SectionHeader dataSectionHeader = new( - start: containerSectionHeader?.EndOffset ?? codeSectionHeader.EndOffset, - size: sectionSizes.DataSectionSize.Value - ); - - header = new EofHeader - { - Version = VERSION, - PrefixSize = HEADER_TERMINATOR_OFFSET, - TypeSection = typeSectionHeader, - CodeSections = codeSectionHeader, - ContainerSections = containerSectionHeader, - DataSection = dataSectionHeader, - }; - - return true; - } - - public bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStrategy strategy) + if (strategy.HasFlag(ValidationStrategy.HasEofMagic) && !code.StartsWith(MAGIC)) { - int startOffset = header.TypeSection.Start; - int endOffset = header.DataSection.Start; - int calculatedCodeLength = - header.TypeSection.Size - + header.CodeSections.Size - + (header.ContainerSections?.Size ?? 0); - CompoundSectionHeader codeSections = header.CodeSections; - ReadOnlySpan contractBody = container[startOffset..endOffset]; - ReadOnlySpan dataBody = container[endOffset..]; - var typeSection = header.TypeSection; - (int typeSectionStart, int typeSectionSize) = (typeSection.Start, typeSection.Size); - - if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) - { - // move this check where `header.ExtraContainers.Count` is parsed - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSections?.Count}"); - return false; - } - - if (contractBody.Length != calculatedCodeLength) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, SectionSizes indicated in bundled header are incorrect, or ContainerCode is incomplete"); - return false; - } - - if (strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size > dataBody.Length) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); - return false; - } - - if (!strategy.HasFlag(ValidationStrategy.AllowTrailingBytes) && strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size != dataBody.Length) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); - return false; - } - - if (codeSections.Count == 0 || codeSections.SubSectionsSizes.Any(size => size == 0)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection size must follow a CodeSection, CodeSection length was {codeSections.Count}"); - return false; - } - - if (codeSections.Count != (typeSectionSize / MINIMUM_TYPESECTION_SIZE)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code Sections count must match TypeSection count, CodeSection count was {codeSections.Count}, expected {typeSectionSize / MINIMUM_TYPESECTION_SIZE}"); - return false; - } - - ReadOnlySpan typesection = container.Slice(typeSectionStart, typeSectionSize); - if (!ValidateTypeSection(typesection)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, invalid typesection found"); - return false; - } - - Span visitedSections = stackalloc bool[header.CodeSections.Count]; - Span visitedContainerSections = stackalloc byte[header.ContainerSections is null ? 1 : 1 + header.ContainerSections.Value.Count]; - visitedContainerSections[0] = strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) - ? (byte)ValidationStrategy.ValidateInitcodeMode - : (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) - ? (byte)ValidationStrategy.ValidateRuntimeMode - : (byte)0); - - visitedSections.Clear(); - - Queue validationQueue = new Queue(); - validationQueue.Enqueue(0); - - while (validationQueue.TryDequeue(out ushort sectionIdx)) - { - if (visitedSections[sectionIdx]) - { - continue; - } - - visitedSections[sectionIdx] = true; - var codeSection = header.CodeSections[sectionIdx]; - - ReadOnlySpan code = container.Slice(header.CodeSections.Start + codeSection.Start, codeSection.Size); - if (!ValidateInstructions(sectionIdx, strategy, typesection, code, header, container, validationQueue, ref visitedContainerSections)) - { - return false; - } - } - - bool HasNoNonReachableSections = - visitedSections[..header.CodeSections.Count].Contains(false) - || (header.ContainerSections is not null && visitedContainerSections[1..header.ContainerSections.Value.Count].Contains((byte)0)); - - return !HasNoNonReachableSections; + eofContainer = null; + return false; } - bool ValidateTypeSection(ReadOnlySpan types) + if (IsEof(code, out byte version) && _eofVersionHandlers.TryGetValue(version, out IEofVersionHandler handler)) { - 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; - } - - 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; - } - - for (int offset = 0; offset < types.Length; offset += MINIMUM_TYPESECTION_SIZE) - { - byte inputCount = types[offset + INPUTS_OFFSET]; - byte outputCount = types[offset + OUTPUTS_OFFSET]; - ushort maxStackHeight = types.Slice(offset + MAX_STACK_HEIGHT_OFFSET, MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16(); - - if (inputCount > INPUTS_MAX) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Too many inputs"); - return false; - } - - if (outputCount > OUTPUTS_MAX && outputCount != NON_RETURNING) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Too many outputs"); - return false; - } - - if (maxStackHeight > MAX_STACK_HEIGHT) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Stack depth too high"); - return false; - } - } - return true; + return handler.TryGetEofContainer(code, strategy, out eofContainer); } - bool ValidateInstructions(ushort sectionId, ValidationStrategy strategy, ReadOnlySpan typesection, ReadOnlySpan code, in EofHeader header, in ReadOnlySpan container, Queue worklist, ref Span visitedContainers) - { - int length = (code.Length / BYTE_BIT_COUNT) + 1; - byte[] codeBitmapArray = ArrayPool.Shared.Rent(length); - byte[] jumpDestsArray = ArrayPool.Shared.Rent(length); - - try - { - // ArrayPool may return a larger array than requested, so we need to slice it to the actual length - Span codeBitmap = codeBitmapArray.AsSpan(0, length); - Span jumpDests = jumpDestsArray.AsSpan(0, length); - // ArrayPool may return a larger array than requested, so we need to slice it to the actual length - codeBitmap.Clear(); - jumpDests.Clear(); - - int pos; - for (pos = 0; pos < code.Length;) - { - Instruction opcode = (Instruction)code[pos]; - int postInstructionByte = pos + 1; - - if (opcode is Instruction.RETURN or Instruction.STOP) - { - if (strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); - return false; - } - else - { - if (visitedContainers[0] == (byte)ValidationStrategy.ValidateInitcodeMode) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); - return false; - } - else - { - visitedContainers[0] = (byte)ValidationStrategy.ValidateRuntimeMode; - } - } - } - - if (opcode is Instruction.RETURNCONTRACT) - { - if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); - return false; - } - else - { - if (visitedContainers[0] == (byte)ValidationStrategy.ValidateRuntimeMode) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); - return false; - } - else - { - visitedContainers[0] = (byte)ValidationStrategy.ValidateInitcodeMode; - } - } - } - - if (!opcode.IsValid(IsEofContext: true)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains undefined opcode {opcode}"); - return false; - } - - if (opcode is Instruction.RJUMP or Instruction.RJUMPI) - { - if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); - return false; - } - - var offset = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthInt16(); - var rjumpdest = offset + TWO_BYTE_LENGTH + postInstructionByte; - - if (rjumpdest < 0 || rjumpdest >= code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Destination outside of Code bounds"); - return false; - } - - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpDests, ref rjumpdest); - BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - } - - if (opcode is Instruction.JUMPF) - { - if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} Argument underflow"); - return false; - } - - var targetSectionId = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); - - if (targetSectionId >= header.CodeSections.Count) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} to unknown code section"); - return false; - } - - byte targetSectionOutputCount = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - byte currentSectionOutputCount = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - bool isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; - - if (!isTargetSectionNonReturning && currentSectionOutputCount < targetSectionOutputCount) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} to code section with more outputs"); - return false; - } - - worklist.Enqueue(targetSectionId); - BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - } - - if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) - { - if (postInstructionByte + ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); - return false; - } - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - - } - - if (opcode is Instruction.RJUMPV) - { - if (postInstructionByte + ONE_BYTE_LENGTH + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Argument underflow"); - return false; - } - - ushort count = (ushort)(code[postInstructionByte] + 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 (postInstructionByte + ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumptable underflow"); - return false; - } - - var immediateValueSize = ONE_BYTE_LENGTH + count * TWO_BYTE_LENGTH; - for (int j = 0; j < count; j++) - { - var offset = code.Slice(postInstructionByte + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthInt16(); - var rjumpdest = offset + immediateValueSize + postInstructionByte; - if (rjumpdest < 0 || rjumpdest >= code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Destination outside of Code bounds"); - return false; - } - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpDests, ref rjumpdest); - } - BitmapHelper.HandleNumbits(immediateValueSize, codeBitmap, ref postInstructionByte); - } - - if (opcode is Instruction.CALLF) - { - if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Argument underflow"); - return false; - } - - ushort targetSectionId = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); - - if (targetSectionId >= header.CodeSections.Count) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Invalid Section Id"); - return false; - } - - byte targetSectionOutputCount = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - if (targetSectionOutputCount == 0x80) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} into non-returning function"); - return false; - } - - worklist.Enqueue(targetSectionId); - BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - } - - if (opcode is Instruction.RETF && typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, non returning sections are not allowed to use opcode {Instruction.RETF}"); - return false; - } - - if (opcode is Instruction.DATALOADN) - { - if (postInstructionByte + TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.DATALOADN} Argument underflow"); - return false; - } - - ushort dataSectionOffset = code.Slice(postInstructionByte, TWO_BYTE_LENGTH).ReadEthUInt16(); - - if (dataSectionOffset + 32 > 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: {header.DataSection.Size / 32}"); - return false; - } - BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - } - - if (opcode is Instruction.RETURNCONTRACT) - { - if (postInstructionByte + ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT} Argument underflow"); - return false; - } - - ushort runtimeContainerId = code[postInstructionByte]; - if (runtimeContainerId >= header.ContainerSections?.Count) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {header.ContainerSections?.Count}"); - return false; - } - - if (visitedContainers[runtimeContainerId + 1] != 0 && visitedContainers[runtimeContainerId + 1] != (byte)ValidationStrategy.ValidateRuntimeMode) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); - return false; - } - - visitedContainers[runtimeContainerId + 1] = (byte)ValidationStrategy.ValidateRuntimeMode; - ReadOnlySpan subcontainer = container.Slice(header.ContainerSections.Value.Start + header.ContainerSections.Value[runtimeContainerId].Start, header.ContainerSections.Value[runtimeContainerId].Size); - - if (!IsValidEof(subcontainer, ValidationStrategy.ValidateRuntimeMode, out _)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate must be a valid Eof"); - return false; - } - - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - } - - if (opcode is Instruction.EOFCREATE) - { - if (postInstructionByte + ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE} Argument underflow"); - return false; - } - - byte initcodeSectionId = code[postInstructionByte]; - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - - if (initcodeSectionId >= header.ContainerSections?.Count) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {header.CodeSections.Count}"); - return false; - } - - if (visitedContainers[initcodeSectionId + 1] != 0 && visitedContainers[initcodeSectionId + 1] != (byte)ValidationStrategy.ValidateInitcodeMode) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s target container can only be a initcode mode bytecode"); - return false; - } - - visitedContainers[initcodeSectionId + 1] = (byte)ValidationStrategy.ValidateInitcodeMode; - ReadOnlySpan subcontainer = container.Slice(header.ContainerSections.Value.Start + header.ContainerSections.Value[initcodeSectionId].Start, header.ContainerSections.Value[initcodeSectionId].Size); - if (!IsValidEof(subcontainer, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateFullBody, out _)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must be a valid Eof"); - return false; - } - - } - - if (opcode is >= Instruction.PUSH0 and <= Instruction.PUSH32) - { - int len = opcode - Instruction.PUSH0; - if (postInstructionByte + len > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} PC Reached out of bounds"); - return false; - } - BitmapHelper.HandleNumbits(len, codeBitmap, ref postInstructionByte); - } - pos = postInstructionByte; - } - - if (pos > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; - } - - bool result = !BitmapHelper.CheckCollision(codeBitmap, jumpDests); - if (!result) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination"); - } - - if (!ValidateStackState(sectionId, code, typesection)) - { - return false; - } - return result; - } - finally - { - ArrayPool.Shared.Return(codeBitmapArray); - ArrayPool.Shared.Return(jumpDestsArray); - } - } - public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection) - { - StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); - recordedStackHeight.Fill(new StackBounds()); - - try - { - ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); - - ushort currrentSectionOutputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - short peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - - int unreachedBytes = code.Length; - bool isTargetSectionNonReturning = false; - - int targetMaxStackHeight = 0; - int programCounter = 0; - recordedStackHeight[0].Max = peakStackHeight; - recordedStackHeight[0].Min = peakStackHeight; - StackBounds currentStackBounds = recordedStackHeight[0]; - - while (programCounter < code.Length) - { - Instruction opcode = (Instruction)code[programCounter]; - (ushort? inputs, ushort? outputs, ushort? immediates) = opcode.StackRequirements(); - - ushort posPostInstruction = (ushort)(programCounter + 1); - if (posPostInstruction > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; - } - - switch (opcode) - { - case Instruction.CALLF or Instruction.JUMPF: - ushort targetSectionId = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); - inputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - - outputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; - outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); - targetMaxStackHeight = typesection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); - - if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callf must not exceed {MAX_STACK_HEIGHT}"); - return false; - } - - if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currrentSectionOutputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currrentSectionOutputs + inputs - outputs} but found {currentStackBounds.Max}"); - return false; - } - break; - case Instruction.DUPN: - int imm_n = 1 + code[posPostInstruction]; - inputs = (ushort)(imm_n); - outputs = (ushort)(inputs + 1); - break; - case Instruction.SWAPN: - imm_n = 1 + code[posPostInstruction]; - outputs = inputs = (ushort)(1 + imm_n); - break; - case Instruction.EXCHANGE: - imm_n = 1 + (byte)(code[posPostInstruction] >> 4); - int imm_m = 1 + (byte)(code[posPostInstruction] & 0x0F); - outputs = inputs = (ushort)(imm_n + imm_m + 1); - break; - } - - if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputs) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputs} but found {currentStackBounds.Min}"); - return false; - } - - if (!opcode.IsTerminating()) - { - short delta = (short)(outputs - inputs); - currentStackBounds.Max += delta; - currentStackBounds.Min += delta; - } - peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); - - switch (opcode) - { - case Instruction.RETF: - { - var 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; - } - break; - } - case Instruction.RJUMP or Instruction.RJUMPI: - { - short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); - int jumpDestination = posPostInstruction + immediates.Value + offset; - - if (opcode is Instruction.RJUMPI) - { - recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); - } - - if (jumpDestination > programCounter) - { - recordedStackHeight[jumpDestination].Combine(currentStackBounds); - } - else - { - if (recordedStackHeight[jumpDestination] != currentStackBounds) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); - return false; - } - } - - break; - } - case Instruction.RJUMPV: - { - var count = code[posPostInstruction] + 1; - immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); - for (short j = 0; j < count; j++) - { - int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; - int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); - int jumpDestination = posPostInstruction + immediates.Value + offset; - if (jumpDestination > programCounter) - { - recordedStackHeight[jumpDestination].Combine(currentStackBounds); - } - else - { - if (recordedStackHeight[jumpDestination] != currentStackBounds) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); - return false; - } - } - } - - posPostInstruction += immediates.Value; - if (posPostInstruction > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; - } - break; - } - } - - unreachedBytes -= 1 + immediates.Value; - programCounter += 1 + immediates.Value; - - if (opcode.IsTerminating()) - { - if (programCounter < code.Length) - { - currentStackBounds = recordedStackHeight[programCounter]; - } - } - else - { - recordedStackHeight[programCounter].Combine(currentStackBounds); - currentStackBounds = recordedStackHeight[programCounter]; - } - } - - 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; - } - - bool result = peakStackHeight < MAX_STACK_HEIGHT; - if (!result) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); - return false; - } - return result; - } - finally - { - ArrayPool.Shared.Return(recordedStackHeight); - } - } + eofContainer = null; + return false; } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index e422c28ac82..06eab0d160a 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -1,11 +1,69 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Evm.EvmObjectFormat.Handlers; using System; using System.Linq; -namespace Nethermind.Evm.EOF; +namespace Nethermind.Evm.EvmObjectFormat; + +public struct EofContainer +{ + public ReadOnlyMemory Container; + public bool IsEmpty => Container.IsEmpty; + + public EofContainer(ReadOnlyMemory container, EofHeader eofHeader) + { + Container = container; + Header = eofHeader; + Prefix = container.Slice(0, 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.Slice(eofHeader.DataSection.Start); + } + + public EofHeader Header; + public ReadOnlyMemory Prefix; + + public ReadOnlyMemory TypeSection; + public ReadOnlyMemory[] TypeSections; + + public ReadOnlyMemory CodeSection; + public ReadOnlyMemory[] CodeSections; + + + public ReadOnlyMemory ContainerSection; + public ReadOnlyMemory[] ContainerSections; + public ReadOnlyMemory DataSection; +} public struct EofHeader() { public required byte Version; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs new file mode 100644 index 00000000000..181430142df --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Evm.EvmObjectFormat; +public enum ValidationStrategy +{ + None = 0, + Validate = 1, + ValidateFullBody = Validate | 2, + ValidateInitcodeMode = Validate | 4, + ValidateRuntimeMode = Validate | 8, + AllowTrailingBytes = Validate | 16, + ExractHeader = 32, + HasEofMagic = 64, + +} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs new file mode 100644 index 00000000000..9618f41b490 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -0,0 +1,1023 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Nethermind.Core.Extensions; +using Nethermind.Logging; +using Nethermind.Evm; +using System.Buffers; +using FastEnumUtility; +using System.Runtime.InteropServices; +using DotNetty.Common.Utilities; +using static Nethermind.Evm.EvmObjectFormat.EofValidator; +using System.Reflection; + +namespace Nethermind.Evm.EvmObjectFormat.Handlers; + +internal class Eof1 : IEofVersionHandler +{ + struct QueueManager + { + public Queue<(int index, ValidationStrategy strategy)> ContainerQueue; + public byte[] VisitedContainers; + + public QueueManager(int containerCount) + { + ContainerQueue = new(); + VisitedContainers = new byte[containerCount]; + + VisitedContainers.Fill((byte)0); + } + + public void Enqueue(int index, ValidationStrategy strategy) + { + ContainerQueue.Enqueue((index, strategy)); + } + + public void MarkVisited(int index, byte strategy) + { + VisitedContainers[index] = (byte)strategy; + } + + public bool TryDequeue(out (int Index, ValidationStrategy Strategy) worklet) => ContainerQueue.TryDequeue(out worklet); + + public bool IsAllVisited() => VisitedContainers.All(x => x != 0); + } + + [StructLayout(LayoutKind.Sequential)] + struct StackBounds() + { + public short Max = -1; + public short Min = 1023; + + public void Combine(StackBounds other) + { + this.Max = Math.Max(this.Max, other.Max); + this.Min = Math.Min(this.Min, other.Min); + } + + public bool BoundsEqual() => Max == Min; + + public static bool operator ==(StackBounds left, StackBounds right) => left.Max == right.Max && right.Min == left.Min; + public static bool operator !=(StackBounds left, StackBounds right) => !(left == right); + public override bool Equals(object obj) => obj is StackBounds && this == (StackBounds)obj; + public override int GetHashCode() => Max ^ Min; + } + + private ref struct Sizes + { + public ushort? TypeSectionSize; + public ushort? CodeSectionSize; + public ushort? DataSectionSize; + public ushort? ContainerSectionSize; + } + + public const byte VERSION = 0x01; + internal enum Separator : byte + { + KIND_TYPE = 0x01, + KIND_CODE = 0x02, + KIND_CONTAINER = 0x03, + KIND_DATA = 0x04, + TERMINATOR = 0x00 + } + + 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 = EofValidator.VERSION_OFFSET + + MINIMUM_HEADER_SECTION_SIZE + + MINIMUM_HEADER_SECTION_SIZE + EofValidator.TWO_BYTE_LENGTH + + MINIMUM_HEADER_SECTION_SIZE + + EofValidator.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 ushort MAXIMUM_NUM_CONTAINER_SECTIONS = 0x00FF; + internal const ushort RETURN_STACK_MAX_HEIGHT = MAXIMUM_NUM_CODE_SECTIONS; // the size in the type sectionn 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 + + public bool TryParseEofHeader(ReadOnlyMemory containerMemory, out EofHeader? header) + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ushort GetUInt16(ReadOnlySpan container, int offset) => + container.Slice(offset, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + + ReadOnlySpan container = containerMemory.Span; + + header = null; + // we need to be able to parse header + minimum section lenghts + 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.StartsWith(EofValidator.MAGIC)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code doesn't start with magic byte sequence expected {EofValidator.MAGIC.ToHexString(true)} "); + return false; + } + + if (container[EofValidator.VERSION_OFFSET] != VERSION) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not Eof version {VERSION}"); + return false; + } + + Sizes sectionSizes = new(); + int[] codeSections = null; + int[] containerSections = null; + int pos = EofValidator.VERSION_OFFSET + 1; + + var continueParsing = true; + while (continueParsing && pos < container.Length) + { + var separator = (Separator)container[pos++]; + + switch (separator) + { + case Separator.KIND_TYPE: + if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + sectionSizes.TypeSectionSize = GetUInt16(container, pos); + if (sectionSizes.TypeSectionSize < MINIMUM_TYPESECTION_SIZE) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); + return false; + } + + pos += EofValidator.TWO_BYTE_LENGTH; + break; + case Separator.KIND_CODE: + if (sectionSizes.TypeSectionSize is null) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); + return false; + } + + if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + var numberOfCodeSections = GetUInt16(container, pos); + sectionSizes.CodeSectionSize = (ushort)(numberOfCodeSections * EofValidator.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; + } + + if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfCodeSections) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + codeSections = new int[numberOfCodeSections]; + int CODESECTION_HEADER_PREFIX_SIZE = pos + EofValidator.TWO_BYTE_LENGTH; + for (ushort i = 0; i < numberOfCodeSections; i++) + { + int currentCodeSizeOffset = CODESECTION_HEADER_PREFIX_SIZE + i * EofValidator.TWO_BYTE_LENGTH; // offset of pos'th code size + int codeSectionSize = GetUInt16(container, currentCodeSizeOffset); + + 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 += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfCodeSections; + break; + case Separator.KIND_CONTAINER: + if (sectionSizes.CodeSectionSize is null) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); + return false; + } + + if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + var numberOfContainerSections = GetUInt16(container, pos); + sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * EofValidator.TWO_BYTE_LENGTH); + if (numberOfContainerSections is > MAXIMUM_NUM_CONTAINER_SECTIONS or 0) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); + return false; + } + + if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfContainerSections) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + containerSections = new int[numberOfContainerSections]; + int CONTAINER_SECTION_HEADER_PREFIX_SIZE = pos + EofValidator.TWO_BYTE_LENGTH; + for (ushort i = 0; i < numberOfContainerSections; i++) + { + int currentContainerSizeOffset = CONTAINER_SECTION_HEADER_PREFIX_SIZE + i * EofValidator.TWO_BYTE_LENGTH; // offset of pos'th code size + int containerSectionSize = GetUInt16(container, currentContainerSizeOffset); + + 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 += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfContainerSections; + break; + case Separator.KIND_DATA: + if (sectionSizes.CodeSectionSize is null) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); + return false; + } + + if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + return false; + } + + sectionSizes.DataSectionSize = GetUInt16(container, pos); + + pos += EofValidator.TWO_BYTE_LENGTH; + break; + case Separator.TERMINATOR: + if (container.Length < pos + EofValidator.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; + } + } + + 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; + } + + var typeSectionSubHeader = new SectionHeader(pos, sectionSizes.TypeSectionSize.Value); + var codeSectionSubHeader = new CompoundSectionHeader(typeSectionSubHeader.EndOffset, codeSections); + CompoundSectionHeader? containerSectionSubHeader = containerSections is null ? null + : new CompoundSectionHeader(codeSectionSubHeader.EndOffset, containerSections); + var dataSectionSubHeader = new SectionHeader(containerSectionSubHeader?.EndOffset ?? codeSectionSubHeader.EndOffset, sectionSizes.DataSectionSize.Value); + + header = new EofHeader + { + Version = VERSION, + PrefixSize = pos, + TypeSection = typeSectionSubHeader, + CodeSections = codeSectionSubHeader, + ContainerSections = containerSectionSubHeader, + DataSection = dataSectionSubHeader, + }; + return true; + } + + public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy validationStrategy, out EofContainer? eofContainer) + { + if (!TryParseEofHeader(code, out EofHeader? header)) + { + eofContainer = null; + return false; + } + + if (!ValidateBody(code.Span, header.Value, validationStrategy)) + { + eofContainer = null; + return false; + } + + eofContainer = new EofContainer(code, header.Value); + + if (validationStrategy.HasFlag(ValidationStrategy.Validate)) + { + if(!ValidateContainer(eofContainer.Value, validationStrategy)) + { + eofContainer = null; + return false; + } + } + + return true; + } + + public bool ValidateContainer(EofContainer eofContainer, ValidationStrategy validationStrategy) + { + QueueManager containerQueue = new(1 + (eofContainer.Header.ContainerSections?.Count ?? 0)); + containerQueue.Enqueue(0, validationStrategy); + + containerQueue.VisitedContainers[0] = validationStrategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) + ? (byte)ValidationStrategy.ValidateInitcodeMode + : validationStrategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) + ? (byte)ValidationStrategy.ValidateRuntimeMode + : (byte)0; + + while (containerQueue.TryDequeue(out var worklet)) + { + EofContainer targetContainer = eofContainer; + if (worklet.Index != 0) + { + if (containerQueue.VisitedContainers[worklet.Index] != 0) + continue; + + if (TryGetEofContainer(targetContainer.ContainerSections[worklet.Index - 1], worklet.Strategy, out EofContainer ? subContainer)) + targetContainer = subContainer.Value; + else + { + return false; + } + + if(!ValidateContainer(targetContainer, worklet.Strategy)) + { + return false; + } + + } else + { + if (!ValidateCodeSections(targetContainer, worklet.Strategy, containerQueue)) + return false; + } + containerQueue.MarkVisited(worklet.Index, (byte)(worklet.Strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) ? ValidationStrategy.ValidateInitcodeMode : ValidationStrategy.ValidateRuntimeMode)); + } + return containerQueue.IsAllVisited(); + } + + bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStrategy strategy) + { + int startOffset = header.TypeSection.Start; + int endOffset = header.DataSection.Start; + int calculatedCodeLength = + header.TypeSection.Size + + header.CodeSections.Size + + (header.ContainerSections?.Size ?? 0); + CompoundSectionHeader codeSections = header.CodeSections; + ReadOnlySpan contractBody = container[startOffset..endOffset]; + ReadOnlySpan dataBody = container[endOffset..]; + var typeSection = header.TypeSection; + (int typeSectionStart, int typeSectionSize) = (typeSection.Start, typeSection.Size); + + if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) + { + // move this check where `header.ExtraContainers.Count` is parsed + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSections?.Count}"); + return false; + } + + if (contractBody.Length != calculatedCodeLength) + { + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, SectionSizes indicated in bundled header are incorrect, or ContainerCode is incomplete"); + return false; + } + + if (strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size > dataBody.Length) + { + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + return false; + } + + if (!strategy.HasFlag(ValidationStrategy.AllowTrailingBytes) && strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size != dataBody.Length) + { + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + return false; + } + + if (codeSections.Count == 0 || codeSections.SubSectionsSizes.Any(size => size == 0)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection size must follow a CodeSection, CodeSection length was {codeSections.Count}"); + return false; + } + + if (codeSections.Count != typeSectionSize / MINIMUM_TYPESECTION_SIZE) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code Sections count must match TypeSection count, CodeSection count was {codeSections.Count}, expected {typeSectionSize / MINIMUM_TYPESECTION_SIZE}"); + return false; + } + + ReadOnlySpan typesection = container.Slice(typeSectionStart, typeSectionSize); + if (!ValidateTypeSection(typesection)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, invalid typesection found"); + return false; + } + + return true; + } + + bool ValidateCodeSections(EofContainer eofContainer, ValidationStrategy strategy, QueueManager containerQueue) + { + QueueManager sectionQueue = new(eofContainer.Header.CodeSections.Count); + + sectionQueue.Enqueue(0, strategy); + + while (sectionQueue.TryDequeue(out var sectionIdx)) + { + if (sectionQueue.VisitedContainers[sectionIdx.Index] != 0) + continue; + + if (!ValidateInstructions(eofContainer, sectionIdx.Index, strategy, sectionQueue, containerQueue)) + return false; + + sectionQueue.MarkVisited(sectionIdx.Index, 1); + } + + return sectionQueue.IsAllVisited(); + } + + bool ValidateTypeSection(ReadOnlySpan types) + { + 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; + } + + 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; + } + + for (var offset = 0; offset < types.Length; offset += MINIMUM_TYPESECTION_SIZE) + { + var inputCount = types[offset + INPUTS_OFFSET]; + var outputCount = types[offset + OUTPUTS_OFFSET]; + ushort maxStackHeight = types.Slice(offset + MAX_STACK_HEIGHT_OFFSET, MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16(); + + if (inputCount > INPUTS_MAX) + { + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Too many inputs"); + return false; + } + + if (outputCount > OUTPUTS_MAX && outputCount != NON_RETURNING) + { + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Too many outputs"); + return false; + } + + if (maxStackHeight > MAX_STACK_HEIGHT) + { + if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Stack depth too high"); + return false; + } + } + return true; + } + + bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationStrategy strategy, QueueManager sectionsWorklist, QueueManager containersWorklist) + { + ReadOnlySpan code = eofContainer.CodeSections[sectionId].Span; + + var length = code.Length / BYTE_BIT_COUNT + 1; + byte[] codeBitmapArray = ArrayPool.Shared.Rent(length); + byte[] jumpDestsArray = ArrayPool.Shared.Rent(length); + + try + { + // ArrayPool may return a larger array than requested, so we need to slice it to the actual length + Span codeBitmap = codeBitmapArray.AsSpan(0, length); + Span jumpDests = jumpDestsArray.AsSpan(0, length); + // ArrayPool may return a larger array than requested, so we need to slice it to the actual length + codeBitmap.Clear(); + jumpDests.Clear(); + + ReadOnlySpan currentTypesection = eofContainer.TypeSections[sectionId].Span; + var isCurrentSectionNonReturning = currentTypesection[OUTPUTS_OFFSET] == 0x80; + + int pos; + for (pos = 0; pos < code.Length;) + { + var opcode = (Instruction)code[pos]; + var postInstructionByte = pos + 1; + + if (opcode is Instruction.RETURN or Instruction.STOP) + { + if (strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); + return false; + } + else + { + if (containersWorklist.VisitedContainers[0] == (byte)ValidationStrategy.ValidateInitcodeMode) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); + return false; + } + else + { + containersWorklist.VisitedContainers[0] = (byte)ValidationStrategy.ValidateRuntimeMode; + } + } + } + + if (!opcode.IsValid(IsEofContext: true)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains undefined opcode {opcode}"); + return false; + } + + if (opcode is Instruction.RJUMP or Instruction.RJUMPI) + { + if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); + return false; + } + + var offset = code.Slice(postInstructionByte, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + var rjumpdest = offset + EofValidator.TWO_BYTE_LENGTH + postInstructionByte; + + if (rjumpdest < 0 || rjumpdest >= code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Destination outside of Code bounds"); + return false; + } + + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDests, ref rjumpdest); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + } + + if (opcode is Instruction.JUMPF) + { + if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} Argument underflow"); + return false; + } + + var targetSectionId = code.Slice(postInstructionByte, EofValidator.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; + + var targetSectionOutputCount = targetTypesection[OUTPUTS_OFFSET]; + var isTargetSectionNonReturning = targetTypesection[OUTPUTS_OFFSET] == 0x80; + var currentSectionOutputCount = currentTypesection[OUTPUTS_OFFSET]; + + if (!isTargetSectionNonReturning && currentSectionOutputCount < targetSectionOutputCount) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} to code section with more outputs"); + return false; + } + + sectionsWorklist.Enqueue(targetSectionId, strategy); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + } + + if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) + { + if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); + return false; + } + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + + } + + if (opcode is Instruction.RJUMPV) + { + if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Argument underflow"); + return false; + } + + var count = (ushort)(code[postInstructionByte] + 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 (postInstructionByte + EofValidator.ONE_BYTE_LENGTH + count * EofValidator.TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumptable underflow"); + return false; + } + + var immediateValueSize = EofValidator.ONE_BYTE_LENGTH + count * EofValidator.TWO_BYTE_LENGTH; + for (var j = 0; j < count; j++) + { + var offset = code.Slice(postInstructionByte + EofValidator.ONE_BYTE_LENGTH + j * EofValidator.TWO_BYTE_LENGTH, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + var rjumpdest = offset + immediateValueSize + postInstructionByte; + if (rjumpdest < 0 || rjumpdest >= code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Destination outside of Code bounds"); + return false; + } + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDests, ref rjumpdest); + } + BitmapHelper.HandleNumbits(immediateValueSize, codeBitmap, ref postInstructionByte); + } + + if (opcode is Instruction.CALLF) + { + if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Argument underflow"); + return false; + } + + ushort targetSectionId = code.Slice(postInstructionByte, EofValidator.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; + + var targetSectionOutputCount = targetTypesection[OUTPUTS_OFFSET]; + + if (targetSectionOutputCount == 0x80) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} into non-returning function"); + return false; + } + + sectionsWorklist.Enqueue(targetSectionId, strategy); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + } + + if (opcode is Instruction.RETF && isCurrentSectionNonReturning) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, non returning sections are not allowed to use opcode {Instruction.RETF}"); + return false; + } + + if (opcode is Instruction.DATALOADN) + { + if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.DATALOADN} Argument underflow"); + return false; + } + + ushort dataSectionOffset = code.Slice(postInstructionByte, EofValidator.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.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + } + + if (opcode is Instruction.RETURNCONTRACT) + { + if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); + return false; + } + else + { + if (containersWorklist.VisitedContainers[0] == (byte)ValidationStrategy.ValidateRuntimeMode) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); + return false; + } + else + { + containersWorklist.VisitedContainers[0] = (byte)ValidationStrategy.ValidateInitcodeMode; + } + } + + if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT} Argument underflow"); + return false; + } + + ushort runtimeContainerId = code[postInstructionByte]; + if (runtimeContainerId >= eofContainer.Header.ContainerSections?.Count) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'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] != (byte)ValidationStrategy.ValidateRuntimeMode) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); + return false; + } + + containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode); + + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + } + + if (opcode is Instruction.EOFCREATE) + { + if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE} Argument underflow"); + return false; + } + + var initcodeSectionId = code[postInstructionByte]; + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + + if (initcodeSectionId >= eofContainer.Header.ContainerSections?.Count) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {eofContainer.Header.CodeSections.Count}"); + return false; + } + + if (containersWorklist.VisitedContainers[initcodeSectionId + 1] != 0 + && containersWorklist.VisitedContainers[initcodeSectionId + 1] != (byte)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.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + } + + if (opcode is >= Instruction.PUSH0 and <= Instruction.PUSH32) + { + int len = opcode - Instruction.PUSH0; + if (postInstructionByte + len > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} PC Reached out of bounds"); + return false; + } + BitmapHelper.HandleNumbits(len, codeBitmap, ref postInstructionByte); + } + pos = postInstructionByte; + } + + if (pos > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); + return false; + } + + var result = !BitmapHelper.CheckCollision(codeBitmap, jumpDests); + if (!result) + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination"); + + if (!ValidateStackState(sectionId, code, eofContainer.TypeSection.Span)) + return false; + return result; + } + finally + { + ArrayPool.Shared.Return(codeBitmapArray); + ArrayPool.Shared.Return(jumpDestsArray); + } + } + public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection) + { + StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); + recordedStackHeight.Fill(new StackBounds()); + + try + { + ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + EofValidator.TWO_BYTE_LENGTH, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + + var currrentSectionOutputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + short peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + + var unreachedBytes = code.Length; + var isTargetSectionNonReturning = false; + + var targetMaxStackHeight = 0; + var programCounter = 0; + recordedStackHeight[0].Max = peakStackHeight; + recordedStackHeight[0].Min = peakStackHeight; + StackBounds currentStackBounds = recordedStackHeight[0]; + + while (programCounter < code.Length) + { + var opcode = (Instruction)code[programCounter]; + (var inputs, var outputs, var immediates) = opcode.StackRequirements(); + + var posPostInstruction = (ushort)(programCounter + 1); + if (posPostInstruction > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); + return false; + } + + switch (opcode) + { + case Instruction.CALLF or Instruction.JUMPF: + ushort targetSectionId = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); + inputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + + outputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; + outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); + targetMaxStackHeight = typesection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + + if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callf must not exceed {MAX_STACK_HEIGHT}"); + return false; + } + + if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currrentSectionOutputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currrentSectionOutputs + inputs - outputs} but found {currentStackBounds.Max}"); + return false; + } + break; + case Instruction.DUPN: + var imm_n = 1 + code[posPostInstruction]; + inputs = (ushort)imm_n; + outputs = (ushort)(inputs + 1); + break; + case Instruction.SWAPN: + imm_n = 1 + code[posPostInstruction]; + outputs = inputs = (ushort)(1 + imm_n); + break; + case Instruction.EXCHANGE: + imm_n = 1 + (byte)(code[posPostInstruction] >> 4); + var imm_m = 1 + (byte)(code[posPostInstruction] & 0x0F); + outputs = inputs = (ushort)(imm_n + imm_m + 1); + break; + } + + if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputs) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputs} but found {currentStackBounds.Min}"); + return false; + } + + if (!opcode.IsTerminating()) + { + var delta = (short)(outputs - inputs); + currentStackBounds.Max += delta; + currentStackBounds.Min += delta; + } + peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); + + switch (opcode) + { + case Instruction.RETF: + { + var 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; + } + break; + } + case Instruction.RJUMP or Instruction.RJUMPI: + { + short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); + var jumpDestination = posPostInstruction + immediates.Value + offset; + + if (opcode is Instruction.RJUMPI) + recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); + + if (jumpDestination > programCounter) + recordedStackHeight[jumpDestination].Combine(currentStackBounds); + else + { + if (recordedStackHeight[jumpDestination] != currentStackBounds) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); + return false; + } + } + + break; + } + case Instruction.RJUMPV: + { + var count = code[posPostInstruction] + 1; + immediates = (ushort)(count * EofValidator.TWO_BYTE_LENGTH + EofValidator.ONE_BYTE_LENGTH); + for (short j = 0; j < count; j++) + { + int case_v = posPostInstruction + EofValidator.ONE_BYTE_LENGTH + j * EofValidator.TWO_BYTE_LENGTH; + int offset = code.Slice(case_v, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + var jumpDestination = posPostInstruction + immediates.Value + offset; + if (jumpDestination > programCounter) + recordedStackHeight[jumpDestination].Combine(currentStackBounds); + else + { + if (recordedStackHeight[jumpDestination] != currentStackBounds) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); + return false; + } + } + } + + posPostInstruction += immediates.Value; + if (posPostInstruction > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); + return false; + } + break; + } + } + + unreachedBytes -= 1 + immediates.Value; + programCounter += 1 + immediates.Value; + + if (opcode.IsTerminating()) + { + if (programCounter < code.Length) + currentStackBounds = recordedStackHeight[programCounter]; + } + else + { + recordedStackHeight[programCounter].Combine(currentStackBounds); + currentStackBounds = recordedStackHeight[programCounter]; + } + } + + 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; + } + + var result = peakStackHeight < MAX_STACK_HEIGHT; + if (!result) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); + return false; + } + return result; + } + finally + { + ArrayPool.Shared.Return(recordedStackHeight); + } + } +} + diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs new file mode 100644 index 00000000000..407a97dfc3b --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs @@ -0,0 +1,12 @@ +// 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, [NotNullWhen(true)] out EofHeader? header); + bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy strategy, [NotNullWhen(true)] out EofContainer? header); +} diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index 36b7ff0db10..4db7c0ee0dc 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using Nethermind.Evm.EOF; +using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.Precompiles; namespace Nethermind.Evm.CodeAnalysis; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index de5fabb20dd..6e17f9ede38 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -3,11 +3,13 @@ using FastEnumUtility; using Nethermind.Core.Specs; -using Nethermind.Evm.EOF; +using Nethermind.Evm.EvmObjectFormat; using Nethermind.Specs.Forks; using System; using System.Diagnostics.CodeAnalysis; +using static Nethermind.Evm.EvmObjectFormat.EofValidator; + namespace Nethermind.Evm { [SuppressMessage("ReSharper", "InconsistentNaming")] @@ -206,7 +208,7 @@ public static int GetImmediateCount(this Instruction instruction, bool IsEofCont => instruction switch { - Instruction.RJUMPV => IsEofContext ? jumpvCount * EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.ONE_BYTE_LENGTH : 0, + Instruction.RJUMPV => IsEofContext ? jumpvCount * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH : 0, >= Instruction.PUSH0 and <= Instruction.PUSH32 => instruction - Instruction.PUSH0, _ => IsEofContext ? instruction.StackRequirements().immediates.Value : 0 }; diff --git a/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs b/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs index f555a6b732e..a464a54dccc 100644 --- a/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs @@ -11,5 +11,15 @@ public static bool StartsWith(this ReadOnlyMemory inputData, byte starting { return inputData.Span[0] == startingByte; } + + public static bool StartsWith(this ReadOnlyMemory inputData, Span startingBytes) + { + return inputData.Span.StartsWith(startingBytes); + } + + public static byte ByteAt(this ReadOnlyMemory inputData, int index) + { + return inputData.Span[index]; + } } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 594e23e9d29..2092bdbf1c8 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -12,7 +12,7 @@ using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Evm.CodeAnalysis; -using Nethermind.Evm.EOF; +using Nethermind.Evm.EvmObjectFormat.Handlers; using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Logging; @@ -20,7 +20,7 @@ using Nethermind.State; using Nethermind.State.Tracing; using static Nethermind.Core.Extensions.MemoryExtensions; - +using static Nethermind.Evm.EvmObjectFormat.EofValidator; using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm.TransactionProcessing @@ -571,13 +571,13 @@ protected void ExecuteEvmCall( // 2 - 2 - update data section size in the header u16 int dataSubheaderSectionStart = - EvmObjectFormat.VERSION_OFFSET // magic + version - + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) - + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) - + (deployCodeInfo.Header.ContainerSections is null + 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) + + (deployCodeInfo.EofContainer.Header.ContainerSections is null ? 0 // container section : (0 bytes if no container section is available) - : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) - + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator + : ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + + ONE_BYTE_LENGTH; // data section seperator ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index a52ecf3a5e8..ec0d35d42ff 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -18,9 +18,9 @@ using Nethermind.Evm.Tracing; using Nethermind.Logging; using Nethermind.State; -using Nethermind.Evm.EOF; using static Nethermind.Evm.VirtualMachine; using static System.Runtime.CompilerServices.Unsafe; +using static Nethermind.Evm.EvmObjectFormat.EofValidator; #if DEBUG using Nethermind.Evm.Tracing.Debugger; @@ -31,12 +31,14 @@ namespace Nethermind.Evm; using Int256; +using Nethermind.Evm.EvmObjectFormat; +using Nethermind.Evm.EvmObjectFormat.Handlers; public class VirtualMachine : IVirtualMachine { - public const int MaxCallDepth = EvmObjectFormat.Eof1.RETURN_STACK_MAX_HEIGHT; + public const int MaxCallDepth = Eof1.RETURN_STACK_MAX_HEIGHT; private readonly static UInt256 P255Int = (UInt256)System.Numerics.BigInteger.Pow(2, 255); - internal readonly static byte[] EofHash256 = KeccakHash.ComputeHashBytes(EvmObjectFormat.MAGIC); + internal readonly static byte[] EofHash256 = KeccakHash.ComputeHashBytes(EofValidator.MAGIC); internal static ref readonly UInt256 P255 => ref P255Int; internal static readonly UInt256 BigInt256 = 256; internal static readonly UInt256 BigInt32 = 32; @@ -351,7 +353,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (previousState.ExecutionType.IsAnyCreateLegacy()) { long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); - bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(spec, callResult.Output.Bytes.Span); + bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(spec, callResult.Output.Bytes); if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { ReadOnlyMemory code = callResult.Output.Bytes; @@ -402,13 +404,13 @@ public TransactionSubstate Run(EvmState state, IWorldState worl // 2 - 2 - update data section size in the header u16 int dataSubheaderSectionStart = - EvmObjectFormat.VERSION_OFFSET // magic + version - + EvmObjectFormat.Eof1.MINIMUM_HEADER_SECTION_SIZE // type section : (1 byte of separator + 2 bytes for size) - + EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.CodeSections.Count // code section : (1 byte of separator + (CodeSections count) * 2 bytes for size) - + (deployCodeInfo.Header.ContainerSections is null + EofValidator.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) + + (deployCodeInfo.EofContainer.Header.ContainerSections is null ? 0 // container section : (0 bytes if no container section is available) - : EvmObjectFormat.ONE_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH + EvmObjectFormat.TWO_BYTE_LENGTH * deployCodeInfo.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) - + EvmObjectFormat.ONE_BYTE_LENGTH; // data section seperator + : ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + + ONE_BYTE_LENGTH; // data section seperator ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); @@ -1424,9 +1426,9 @@ private CallResult ExecuteCode externalCode = _codeInfoRepository.GetCachedCodeInfo(_state, address, spec).MachineCode; - if (spec.IsEofEnabled && EvmObjectFormat.IsEof(externalCode.Span, out _)) + if (spec.IsEofEnabled && IsEof(externalCode, out _)) { - slice = EOF.EvmObjectFormat.MAGIC.SliceWithZeroPadding(b, (int)result); + slice = EofValidator.MAGIC.SliceWithZeroPadding(b, (int)result); } else { @@ -2074,8 +2076,8 @@ private CallResult ExecuteCode account = _state.GetCode(address); - if (spec.IsEofEnabled && EvmObjectFormat.IsEof(account, out _)) + Memory account = _state.GetCode(address); + if (spec.IsEofEnabled && IsEof(account, out _)) { stack.PushBytes(EofHash256); } @@ -2161,8 +2163,8 @@ private CallResult ExecuteCode 0) { if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) goto OutOfGas; - short offset = codeSection.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); - programCounter += EvmObjectFormat.TWO_BYTE_LENGTH + offset; + short offset = codeSection.Slice(programCounter, TWO_BYTE_LENGTH).ReadEthInt16(); + programCounter += TWO_BYTE_LENGTH + offset; break; } goto InvalidInstruction; @@ -2173,12 +2175,12 @@ private CallResult ExecuteCode condition = stack.PopWord256(); - short offset = codeSection.Slice(programCounter, EvmObjectFormat.TWO_BYTE_LENGTH).ReadEthInt16(); + short offset = codeSection.Slice(programCounter, TWO_BYTE_LENGTH).ReadEthInt16(); if (!condition.SequenceEqual(BytesZero32)) { programCounter += offset; } - programCounter += EvmObjectFormat.TWO_BYTE_LENGTH; + programCounter += TWO_BYTE_LENGTH; break; } goto InvalidInstruction; @@ -2191,11 +2193,11 @@ private CallResult ExecuteCode UInt16.MaxValue) + if (projectedNewSize < deploycodeInfo.EofContainer.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) { goto AccessViolation; } @@ -2345,11 +2347,11 @@ private CallResult ExecuteCode private void InstructionExtCodeSize(Address address, ref EvmStack stack, IReleaseSpec spec) where TTracingInstructions : struct, IIsTracing { ReadOnlyMemory accountCode = _codeInfoRepository.GetCachedCodeInfo(_state, address, spec).MachineCode; - if (spec.IsEofEnabled && EvmObjectFormat.IsEof(accountCode.Span, out _)) + if (spec.IsEofEnabled && IsEof(accountCode, out _)) { stack.PushUInt256(2); } @@ -3126,7 +3128,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid // for another tx as returned to pool. - if (spec.IsEofEnabled && initCode.Span.StartsWith(EvmObjectFormat.MAGIC)) + if (spec.IsEofEnabled && initCode.Span.StartsWith(EofValidator.MAGIC)) { _returnDataBuffer = Array.Empty(); stack.PushZero(); From d9c16c408cffd007777b1b38becea7db8a1f3f96 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 26 Aug 2024 11:50:09 +0100 Subject: [PATCH 134/255] * index out of range fix --- .../EvmObjectFormat/Handlers/EofV1.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 9618f41b490..6299930e7fe 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -402,6 +402,13 @@ bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStra + header.CodeSections.Size + (header.ContainerSections?.Size ?? 0); CompoundSectionHeader codeSections = header.CodeSections; + + if(endOffset > container.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + return false; + } + ReadOnlySpan contractBody = container[startOffset..endOffset]; ReadOnlySpan dataBody = container[endOffset..]; var typeSection = header.TypeSection; @@ -932,7 +939,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); var jumpDestination = posPostInstruction + immediates.Value + offset; - if (opcode is Instruction.RJUMPI) + if (opcode is Instruction.RJUMPI && (posPostInstruction + immediates.Value >= recordedStackHeight.Length)) recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); if (jumpDestination > programCounter) @@ -989,8 +996,11 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea } else { - recordedStackHeight[programCounter].Combine(currentStackBounds); - currentStackBounds = recordedStackHeight[programCounter]; + if (programCounter < code.Length) + { + recordedStackHeight[programCounter].Combine(currentStackBounds); + currentStackBounds = recordedStackHeight[programCounter]; + } } } From e098dcba0c75330ce71bad12fd6344c7c0e51e02 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 26 Aug 2024 13:35:00 +0100 Subject: [PATCH 135/255] * remove unnecessary double check --- .../Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 6299930e7fe..d9a11d712e2 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -371,18 +371,8 @@ public bool ValidateContainer(EofContainer eofContainer, ValidationStrategy vali if (containerQueue.VisitedContainers[worklet.Index] != 0) continue; - if (TryGetEofContainer(targetContainer.ContainerSections[worklet.Index - 1], worklet.Strategy, out EofContainer ? subContainer)) - targetContainer = subContainer.Value; - else - { - return false; - } - - if(!ValidateContainer(targetContainer, worklet.Strategy)) - { + if (!TryGetEofContainer(targetContainer.ContainerSections[worklet.Index - 1], worklet.Strategy, out EofContainer ? subContainer)) return false; - } - } else { if (!ValidateCodeSections(targetContainer, worklet.Strategy, containerQueue)) From c5080f7b89b8ede279656a5966851822ebb37a6b Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 26 Aug 2024 13:42:22 +0100 Subject: [PATCH 136/255] * fix wrong boundary check --- src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index d9a11d712e2..5135e739b74 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -929,7 +929,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); var jumpDestination = posPostInstruction + immediates.Value + offset; - if (opcode is Instruction.RJUMPI && (posPostInstruction + immediates.Value >= recordedStackHeight.Length)) + if (opcode is Instruction.RJUMPI && (posPostInstruction + immediates.Value < recordedStackHeight.Length)) recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); if (jumpDestination > programCounter) From 0f115b4a4e2af7f5e5c33ea69c6a238c60529607 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 26 Aug 2024 13:47:43 +0100 Subject: [PATCH 137/255] * Added EOFCREATE / RETURNCONTRACT null checks --- .../Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 5135e739b74..a052a0b9aa5 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -746,7 +746,7 @@ bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationSt } ushort runtimeContainerId = code[postInstructionByte]; - if (runtimeContainerId >= eofContainer.Header.ContainerSections?.Count) + if (eofContainer.Header.ContainerSections is null || runtimeContainerId >= eofContainer.Header.ContainerSections?.Count) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {eofContainer.Header.ContainerSections?.Count}"); return false; @@ -775,7 +775,7 @@ bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationSt var initcodeSectionId = code[postInstructionByte]; BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); - if (initcodeSectionId >= eofContainer.Header.ContainerSections?.Count) + if (eofContainer.Header.ContainerSections is null || initcodeSectionId >= eofContainer.Header.ContainerSections?.Count) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {eofContainer.Header.CodeSections.Count}"); return false; From 43559a90c77919e6744f57a84e84a34bfa8fb0a9 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 3 Sep 2024 11:12:59 +0100 Subject: [PATCH 138/255] Add new EOF tests format for Pyspec tests (#7344) Co-authored-by: Ben Adams --- .../LoadPyspecTestsStrategy.cs | 37 ++++++-- .../PragueEofTests.cs | 28 ++++++ src/Nethermind/Ethereum.Test.Base/EofTest.cs | 27 ++++++ .../Ethereum.Test.Base/EofTestBase.cs | 90 +++++++++++++++++++ .../Ethereum.Test.Base/EofTestJson.cs | 17 ++++ .../Ethereum.Test.Base/FileTestsSource.cs | 25 +++++- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 29 +++++- .../Ethereum.Test.Base/TestResultJson.cs | 9 ++ .../Ethereum.Test.Base/VectorTestJson.cs | 12 +++ .../EvmObjectFormat/Handlers/EofV1.cs | 11 +-- .../ReadOnlyMemoryExtensions.cs | 3 +- src/tests | 2 +- 12 files changed, 272 insertions(+), 18 deletions(-) create mode 100644 src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs create mode 100644 src/Nethermind/Ethereum.Test.Base/EofTest.cs create mode 100644 src/Nethermind/Ethereum.Test.Base/EofTestBase.cs create mode 100644 src/Nethermind/Ethereum.Test.Base/EofTestJson.cs create mode 100644 src/Nethermind/Ethereum.Test.Base/TestResultJson.cs create mode 100644 src/Nethermind/Ethereum.Test.Base/VectorTestJson.cs diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/LoadPyspecTestsStrategy.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/LoadPyspecTestsStrategy.cs index ee879258d11..705c06f751c 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/LoadPyspecTestsStrategy.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/LoadPyspecTestsStrategy.cs @@ -15,6 +15,12 @@ namespace Ethereum.Blockchain.Pyspec.Test; public class LoadPyspecTestsStrategy : ITestLoadStrategy { + private enum TestType + { + Blockchain, + GeneralState, + Eof + } public string ArchiveVersion { get; init; } = Constants.DEFAULT_ARCHIVE_VERSION; public string ArchiveName { get; init; } = Constants.DEFAULT_ARCHIVE_NAME; @@ -23,11 +29,16 @@ public IEnumerable Load(string testsDir, string wildcard = null) string testsDirectoryName = Path.Combine(AppContext.BaseDirectory, "PyTests", ArchiveVersion, ArchiveName.Split('.')[0]); if (!Directory.Exists(testsDirectoryName)) // Prevent redownloading the fixtures if they already exists with this version and archive name DownloadAndExtract(ArchiveVersion, ArchiveName, testsDirectoryName); - bool isStateTest = testsDir.Contains("state_tests", StringComparison.InvariantCultureIgnoreCase); + TestType testType = testsDir.Contains("state_tests", StringComparison.InvariantCultureIgnoreCase) + ? TestType.GeneralState + : testsDir.Contains("eof_tests", StringComparison.InvariantCultureIgnoreCase) + ? TestType.Eof + : TestType.Blockchain; + IEnumerable testDirs = !string.IsNullOrEmpty(testsDir) ? Directory.EnumerateDirectories(Path.Combine(testsDirectoryName, testsDir), "*", new EnumerationOptions { RecurseSubdirectories = true }) : Directory.EnumerateDirectories(testsDirectoryName, "*", new EnumerationOptions { RecurseSubdirectories = true }); - return testDirs.SelectMany(td => LoadTestsFromDirectory(td, wildcard, isStateTest)); + return testDirs.SelectMany(td => LoadTestsFromDirectory(td, wildcard, testType)); } private void DownloadAndExtract(string archiveVersion, string archiveName, string testsDirectoryName) @@ -44,7 +55,7 @@ private void DownloadAndExtract(string archiveVersion, string archiveName, strin TarFile.ExtractToDirectory(gzStream, testsDirectoryName, true); } - private IEnumerable LoadTestsFromDirectory(string testDir, string wildcard, bool isStateTest) + private IEnumerable LoadTestsFromDirectory(string testDir, string wildcard, TestType testType) { List testsByName = new(); IEnumerable testFiles = Directory.EnumerateFiles(testDir); @@ -54,9 +65,13 @@ private IEnumerable LoadTestsFromDirectory(string testDir, string FileTestsSource fileTestsSource = new(testFile, wildcard); try { - IEnumerable tests = isStateTest - ? fileTestsSource.LoadGeneralStateTests() - : fileTestsSource.LoadBlockchainTests(); + IEnumerable tests = testType switch + { + TestType.Eof => fileTestsSource.LoadEofTests(), + TestType.GeneralState => fileTestsSource.LoadGeneralStateTests(), + _ => fileTestsSource.LoadBlockchainTests() + }; + foreach (IEthereumTest test in tests) { test.Category = testDir; @@ -65,9 +80,13 @@ private IEnumerable LoadTestsFromDirectory(string testDir, string } catch (Exception e) { - IEthereumTest failedTest = isStateTest - ? new GeneralStateTest() - : new BlockchainTest(); + IEthereumTest failedTest = testType switch + { + TestType.Eof => new EofTest(), + TestType.GeneralState => new GeneralStateTest(), + _ => new BlockchainTest() + }; + failedTest.Name = testDir; failedTest.LoadFailure = $"Failed to load: {e}"; testsByName.Add(failedTest); diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs new file mode 100644 index 00000000000..58200b6e53d --- /dev/null +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ethereum.Test.Base; +using NUnit.Framework; + +namespace Ethereum.Blockchain.Pyspec.Test; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class PragueEofTests : EofTestBase +{ + [TestCaseSource(nameof(LoadTests))] + public void Test(EofTest test) => RunTest(test); + + private static IEnumerable LoadTests() + { + TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() + { + ArchiveName = "fixtures_eip7692.tar.gz", + ArchiveVersion = "eip7692@v1.0.8" + }, $"fixtures/eof_tests/prague"); + return loader.LoadTests().Cast(); + } +} diff --git a/src/Nethermind/Ethereum.Test.Base/EofTest.cs b/src/Nethermind/Ethereum.Test.Base/EofTest.cs new file mode 100644 index 00000000000..591a7edf435 --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/EofTest.cs @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Ethereum.Test.Base.Interfaces; +using System.Collections.Generic; +using System.Numerics; + +namespace Ethereum.Test.Base; +public class Result +{ + public bool Success { get; set; } + public string? Error { get; set; } +} + +public class VectorTest +{ + public byte[] Code { get; set; } + public Dictionary Results { get; set; } +} + +public class EofTest : IEthereumTest +{ + public string Name { get; set; } + public VectorTest[] Vectors { get; set; } + public string? Category { get; set; } + public string? LoadFailure { get; set; } +} diff --git a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs new file mode 100644 index 00000000000..f7752af46a0 --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Nethermind.Blockchain; +using Nethermind.Consensus.Ethash; +using Nethermind.Consensus.Validators; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; +using Nethermind.Db; +using Nethermind.Int256; +using Nethermind.Evm; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Logging; +using Nethermind.Specs; +using Nethermind.Specs.Forks; +using Nethermind.Specs.Test; +using Nethermind.State; +using Nethermind.Trie.Pruning; +using NUnit.Framework; +using System.Threading.Tasks; +using Nethermind.TxPool; + +namespace Ethereum.Test.Base +{ + public abstract class EofTestBase + { + private static ILogger _logger = new(new ConsoleAsyncLogger(LogLevel.Info)); + private static ILogManager _logManager = new TestLogManager(LogLevel.Warn); + + [SetUp] + public void Setup() + { + } + + protected static void Setup(ILogManager logManager) + { + _logManager = logManager ?? LimboLogs.Instance; + _logger = _logManager.GetClassLogger(); + } + + protected bool RunTest(EofTest test) + { + return RunTest(test, NullTxTracer.Instance); + } + + protected bool RunTest(EofTest test, ITxTracer txTracer) + { + TestContext.Write($"Running {test.Name} at {DateTime.UtcNow:HH:mm:ss.ffffff}"); + Assert.IsNull(test.LoadFailure, "test data loading failure"); + + + List results = new(); + foreach (var vector in test.Vectors) + { + var code = vector.Code; + foreach (var kvp in vector.Results) + { + var fork = kvp.Key switch + { + "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 {kvp.Key} is not supported") + }; + + bool result = CodeDepositHandler.IsValidWithEofRules(fork, code, 1); + results.Add(result == kvp.Value.Success); + } + + } + + return results.TrueForAll(r => r); + } + } +} diff --git a/src/Nethermind/Ethereum.Test.Base/EofTestJson.cs b/src/Nethermind/Ethereum.Test.Base/EofTestJson.cs new file mode 100644 index 00000000000..0e2a4a6077c --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/EofTestJson.cs @@ -0,0 +1,17 @@ +// 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 2e0baece45e..c72b53f7429 100644 --- a/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs +++ b/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs @@ -20,6 +20,29 @@ public FileTestsSource(string fileName, string? wildcard = null) _wildcard = wildcard; } + public IEnumerable LoadEofTests() + { + try + { + if (Path.GetFileName(_fileName).StartsWith(".")) + { + return Enumerable.Empty(); + } + + if (_wildcard is not null && !_fileName.Contains(_wildcard)) + { + return Enumerable.Empty(); + } + + string json = File.ReadAllText(_fileName); + return JsonToEthereumTest.ConvertToEofTests(json); + } + catch (Exception e) + { + return Enumerable.Repeat(new EofTest { Name = _fileName, LoadFailure = $"Failed to load: {e}" }, 1); + } + } + public IEnumerable LoadGeneralStateTests() { try @@ -35,7 +58,7 @@ public IEnumerable LoadGeneralStateTests() } string json = File.ReadAllText(_fileName); - return JsonToEthereumTest.Convert(json); + return JsonToEthereumTest.ConvertStateTest(json); } catch (Exception e) { diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index d180fa40dd8..df0372649bd 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -283,7 +283,34 @@ public static BlockchainTest Convert(string name, BlockchainTestJson testJson) private static readonly EthereumJsonSerializer _serializer = new(); - public static IEnumerable Convert(string json) + public static IEnumerable ConvertToEofTests(string json) + { + Dictionary testsInFile = _serializer.Deserialize>(json); + List tests = new(); + foreach (KeyValuePair namedTest in testsInFile) + { + EofTest test = new(); + test.Name = namedTest.Key; + test.Vectors = namedTest.Value.Vectors.Select(pair => + { + VectorTestJson vectorJson = pair.Value; + VectorTest vector = new(); + vector.Code = Bytes.FromHexString(vectorJson.Code); + vector.Results = vectorJson.Results.ToDictionary( + p => p.Key, + p => p.Value.Result + ? new Result { Success = true } + : new Result { Success = false, Error = p.Value.Exception } + ); + return vector; + }).ToArray(); + tests.Add(test); + } + + return tests; + } + + public static IEnumerable ConvertStateTest(string json) { Dictionary testsInFile = _serializer.Deserialize>(json); diff --git a/src/Nethermind/Ethereum.Test.Base/TestResultJson.cs b/src/Nethermind/Ethereum.Test.Base/TestResultJson.cs new file mode 100644 index 00000000000..3255830ae71 --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/TestResultJson.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Ethereum.Test.Base; +public class TestResultJson +{ + public string? Exception { get; set; } + public bool Result { get; set; } +} diff --git a/src/Nethermind/Ethereum.Test.Base/VectorTestJson.cs b/src/Nethermind/Ethereum.Test.Base/VectorTestJson.cs new file mode 100644 index 00000000000..db2d6398892 --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/VectorTestJson.cs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; + +namespace Ethereum.Test.Base; + +public class VectorTestJson +{ + public string Code { get; set; } + public Dictionary Results { get; set; } +} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index a052a0b9aa5..75fcdaea0e2 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -342,12 +342,12 @@ public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy val if (validationStrategy.HasFlag(ValidationStrategy.Validate)) { - if(!ValidateContainer(eofContainer.Value, validationStrategy)) + if (!ValidateContainer(eofContainer.Value, validationStrategy)) { eofContainer = null; return false; } - } + } return true; } @@ -371,9 +371,10 @@ public bool ValidateContainer(EofContainer eofContainer, ValidationStrategy vali if (containerQueue.VisitedContainers[worklet.Index] != 0) continue; - if (!TryGetEofContainer(targetContainer.ContainerSections[worklet.Index - 1], worklet.Strategy, out EofContainer ? subContainer)) + if (!TryGetEofContainer(targetContainer.ContainerSections[worklet.Index - 1], worklet.Strategy, out EofContainer? subContainer)) return false; - } else + } + else { if (!ValidateCodeSections(targetContainer, worklet.Strategy, containerQueue)) return false; @@ -393,7 +394,7 @@ bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStra + (header.ContainerSections?.Size ?? 0); CompoundSectionHeader codeSections = header.CodeSections; - if(endOffset > container.Length) + if (endOffset > container.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); return false; diff --git a/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs b/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs index a464a54dccc..b36a8ce9ac0 100644 --- a/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs @@ -9,7 +9,8 @@ public static class ReadOnlyMemoryExtensions { public static bool StartsWith(this ReadOnlyMemory inputData, byte startingByte) { - return inputData.Span[0] == startingByte; + ReadOnlySpan span = inputData.Span; + return span.Length > 0 && span[0] == startingByte; } public static bool StartsWith(this ReadOnlyMemory inputData, Span startingBytes) diff --git a/src/tests b/src/tests index 8c215d6b56f..27a008d13ec 160000 --- a/src/tests +++ b/src/tests @@ -1 +1 @@ -Subproject commit 8c215d6b56fed36501d04c165093357f102de2ac +Subproject commit 27a008d13ecb0718b300e8f055cc73357a11e96a From 6f762b8211aa79dd9a09c64b753a26dd95df1b72 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 3 Sep 2024 12:15:13 +0100 Subject: [PATCH 139/255] Add EOFTest CLI Support (#7383) Co-authored-by: Danno Ferrin --- .../Interfaces/IEofTestRunner.cs | 12 +++ .../LoadEofTestsFileStrategy.cs | 56 ++++++++++++++ src/Nethermind/EthereumTests.sln | 6 ++ .../Nethermind.EOFParse.Runner.csproj | 24 ++++++ .../Nethermind.EOFParse.Runner/Program.cs | 75 +++++++++++++++++++ .../Properties/launchSettings.json | 21 ++++++ .../Nethermind.Test.Runner/EofTestsRunner.cs | 60 +++++++++++++++ .../Nethermind.Test.Runner.csproj | 3 + .../Nethermind.Test.Runner/Program.cs | 14 ++++ .../Properties/launchSettings.json | 4 + .../Nethermind.Test.Runner/eoftest1.json | 23 ++++++ 11 files changed, 298 insertions(+) create mode 100644 src/Nethermind/Ethereum.Test.Base/Interfaces/IEofTestRunner.cs create mode 100644 src/Nethermind/Ethereum.Test.Base/LoadEofTestsFileStrategy.cs create mode 100644 src/Nethermind/Nethermind.EOFParse.Runner/Nethermind.EOFParse.Runner.csproj create mode 100644 src/Nethermind/Nethermind.EOFParse.Runner/Program.cs create mode 100644 src/Nethermind/Nethermind.EOFParse.Runner/Properties/launchSettings.json create mode 100644 src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs create mode 100644 src/Nethermind/Nethermind.Test.Runner/eoftest1.json diff --git a/src/Nethermind/Ethereum.Test.Base/Interfaces/IEofTestRunner.cs b/src/Nethermind/Ethereum.Test.Base/Interfaces/IEofTestRunner.cs new file mode 100644 index 00000000000..ae82bd923eb --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/Interfaces/IEofTestRunner.cs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; + +namespace Ethereum.Test.Base.Interfaces +{ + public interface IEofTestRunner + { + IEnumerable RunTests(); + } +} diff --git a/src/Nethermind/Ethereum.Test.Base/LoadEofTestsFileStrategy.cs b/src/Nethermind/Ethereum.Test.Base/LoadEofTestsFileStrategy.cs new file mode 100644 index 00000000000..1bf9ac0d1b0 --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/LoadEofTestsFileStrategy.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.IO; +using Ethereum.Test.Base.Interfaces; + +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.LoadEofTests(); + + 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); + try + { + IEnumerable tests = fileTestsSource.LoadEofTests(); + + eofTests.AddRange(tests); + } + catch (Exception e) + { + eofTests.Add(new EofTest() { Name = testFile, LoadFailure = $"Failed to load: {e}" }); + } + } + + return eofTests; + } + + private string GetEofTestsDirectory() + { + string currentDirectory = AppDomain.CurrentDomain.BaseDirectory; + + return Path.Combine(currentDirectory.Remove(currentDirectory.LastIndexOf("src")), "src", "tests", "EOFTests"); + } + } +} diff --git a/src/Nethermind/EthereumTests.sln b/src/Nethermind/EthereumTests.sln index 10f3a923f34..57ed3cdbc96 100644 --- a/src/Nethermind/EthereumTests.sln +++ b/src/Nethermind/EthereumTests.sln @@ -115,6 +115,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Synchronization" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Sockets", "Nethermind.Sockets\Nethermind.Sockets.csproj", "{E9D67F92-D848-4DB3-A586-2AC6DE2A3933}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.EOFParse.Runner", "Nethermind.EOFParse.Runner\Nethermind.EOFParse.Runner.csproj", "{47773DCF-2892-4F87-993F-1A0912CC6420}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -341,6 +343,10 @@ Global {E9D67F92-D848-4DB3-A586-2AC6DE2A3933}.Debug|Any CPU.Build.0 = Debug|Any CPU {E9D67F92-D848-4DB3-A586-2AC6DE2A3933}.Release|Any CPU.ActiveCfg = Release|Any CPU {E9D67F92-D848-4DB3-A586-2AC6DE2A3933}.Release|Any CPU.Build.0 = Release|Any CPU + {47773DCF-2892-4F87-993F-1A0912CC6420}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47773DCF-2892-4F87-993F-1A0912CC6420}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47773DCF-2892-4F87-993F-1A0912CC6420}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47773DCF-2892-4F87-993F-1A0912CC6420}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Nethermind/Nethermind.EOFParse.Runner/Nethermind.EOFParse.Runner.csproj b/src/Nethermind/Nethermind.EOFParse.Runner/Nethermind.EOFParse.Runner.csproj new file mode 100644 index 00000000000..973322a044a --- /dev/null +++ b/src/Nethermind/Nethermind.EOFParse.Runner/Nethermind.EOFParse.Runner.csproj @@ -0,0 +1,24 @@ + + + + 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 new file mode 100644 index 00000000000..09b3ca89e87 --- /dev/null +++ b/src/Nethermind/Nethermind.EOFParse.Runner/Program.cs @@ -0,0 +1,75 @@ +using System; +using System.Linq; +using CommandLine; +using Nethermind.Core.Extensions; +using Nethermind.Evm.EvmObjectFormat; + +namespace Nethermind.EOFParse.Runner +{ + internal class Program + { + public class Options + { + [Option('i', "input", Required = false, + HelpText = "Raw eof input")] + public string Input { get; set; } + + [Option('x', "stdin", Required = false, + HelpText = + "Interactive testing mode.")] + public bool Stdin { get; set; } + } + + public static void Main(params string[] args) + { + ParserResult result = Parser.Default.ParseArguments(args); + if (result is Parsed options) + Run(options.Value); + } + + private static void Run(Options options) + { + string input = options.Input; + if (options.Stdin || input?.Length == 0) + { + 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 (!options.Stdin) + break; + } + + input = Console.ReadLine(); + } + } + } +} diff --git a/src/Nethermind/Nethermind.EOFParse.Runner/Properties/launchSettings.json b/src/Nethermind/Nethermind.EOFParse.Runner/Properties/launchSettings.json new file mode 100644 index 00000000000..e60316dc652 --- /dev/null +++ b/src/Nethermind/Nethermind.EOFParse.Runner/Properties/launchSettings.json @@ -0,0 +1,21 @@ +{ + "$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.Test.Runner/EofTestsRunner.cs b/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs new file mode 100644 index 00000000000..ea684283a49 --- /dev/null +++ b/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs @@ -0,0 +1,60 @@ +// 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; +using Ethereum.Test.Base.Interfaces; + +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 = (IEnumerable)_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, "Prague", 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 fd96da1e534..0bf19eb1df5 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj +++ b/src/Nethermind/Nethermind.Test.Runner/Nethermind.Test.Runner.csproj @@ -26,6 +26,9 @@ PreserveNewest + + PreserveNewest + Always diff --git a/src/Nethermind/Nethermind.Test.Runner/Program.cs b/src/Nethermind/Nethermind.Test.Runner/Program.cs index ccb60229cb5..ec1ed824372 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Test.Runner/Program.cs @@ -23,6 +23,9 @@ public class Options [Option('b', "blockTest", Required = false, HelpText = "Set test as blockTest. if not, it will be by default assumed a state test.")] public bool BlockTest { get; set; } + [Option('e', "eofTest", Required = false, HelpText = "Set test as eofTest. if not, it will be by default assumed a state test.")] + public bool EofTest { get; set; } + [Option('t', "trace", Required = false, HelpText = "Set to always trace (by default traces are only generated for failing tests). [Only for State Test]")] public bool TraceAlways { get; set; } @@ -64,8 +67,11 @@ private static async Task Run(Options options) while (!string.IsNullOrWhiteSpace(input)) { + if (options.BlockTest) await RunBlockTest(input, source => new BlockchainTestsRunner(source, options.Filter)); + else if (options.EofTest) + RunEofTest(input, source => new EofTestsRunner(source, options.Filter)); else RunStateTest(input, source => new StateTestsRunner(source, whenTrace, !options.ExcludeMemory, !options.ExcludeStack, options.Filter)); if (!options.Stdin) @@ -86,6 +92,14 @@ 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 c2bffc4ce24..14ada1706b6 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Properties/launchSettings.json +++ b/src/Nethermind/Nethermind.Test.Runner/Properties/launchSettings.json @@ -8,6 +8,10 @@ "commandName": "Project", "commandLineArgs": "-b -i blockchainTest1.json" }, + "EOF Test": { + "commandName": "Project", + "commandLineArgs": "-e -i eoftest1.json" + }, "Regex State Test": { "commandName": "Project", "commandLineArgs": "-i statetest1.json -f \"randomStatetestmartin-Wed_10_02_29-14338-([0-9]+)\"" diff --git a/src/Nethermind/Nethermind.Test.Runner/eoftest1.json b/src/Nethermind/Nethermind.Test.Runner/eoftest1.json new file mode 100644 index 00000000000..b9688e00a41 --- /dev/null +++ b/src/Nethermind/Nethermind.Test.Runner/eoftest1.json @@ -0,0 +1,23 @@ +{ + "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" + } + } +} From 56377eafd234ef1b01d76d53217f68dcc1969e0f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 5 Sep 2024 05:52:22 +0100 Subject: [PATCH 140/255] Fix stackoverflow in tests --- .../PragueEofTests.cs | 1 - .../EvmObjectFormat/EofCodeValidator.cs | 8 -- .../EvmObjectFormat/EofHeader.cs | 52 +++---- .../EvmObjectFormat/EofValidationStrategy.cs | 9 +- .../EvmObjectFormat/Handlers/EofV1.cs | 132 +++++++++++------- 5 files changed, 106 insertions(+), 96 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs index 58200b6e53d..cc62ab55a43 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Ethereum.Test.Base; using NUnit.Framework; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index b26a9a72884..06185b7acc0 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -2,17 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Buffers; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using DotNetty.Common.Utilities; -using FastEnumUtility; -using Nethermind.Core.Extensions; -using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.EvmObjectFormat.Handlers; using Nethermind.Logging; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index 06eab0d160a..4211222949d 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -1,16 +1,15 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using Nethermind.Evm.EvmObjectFormat.Handlers; using System; using System.Linq; +using Nethermind.Evm.EvmObjectFormat.Handlers; namespace Nethermind.Evm.EvmObjectFormat; - -public struct EofContainer +public readonly struct EofContainer { - public ReadOnlyMemory Container; + public readonly ReadOnlyMemory Container; public bool IsEmpty => Container.IsEmpty; public EofContainer(ReadOnlyMemory container, EofHeader eofHeader) @@ -50,19 +49,19 @@ public EofContainer(ReadOnlyMemory container, EofHeader eofHeader) DataSection = container.Slice(eofHeader.DataSection.Start); } - public EofHeader Header; - public ReadOnlyMemory Prefix; + public readonly EofHeader Header; + public readonly ReadOnlyMemory Prefix; - public ReadOnlyMemory TypeSection; - public ReadOnlyMemory[] TypeSections; + public readonly ReadOnlyMemory TypeSection; + public readonly ReadOnlyMemory[] TypeSections; - public ReadOnlyMemory CodeSection; - public ReadOnlyMemory[] CodeSections; + public readonly ReadOnlyMemory CodeSection; + public readonly ReadOnlyMemory[] CodeSections; - public ReadOnlyMemory ContainerSection; - public ReadOnlyMemory[] ContainerSections; - public ReadOnlyMemory DataSection; + public readonly ReadOnlyMemory ContainerSection; + public readonly ReadOnlyMemory[] ContainerSections; + public readonly ReadOnlyMemory DataSection; } public struct EofHeader() { @@ -74,7 +73,7 @@ public struct EofHeader() public required SectionHeader DataSection; } -public struct SectionHeader(int start, ushort size) +public readonly struct SectionHeader(int start, ushort size) { public readonly int Start => start; public readonly int Size => size; @@ -83,7 +82,7 @@ public struct SectionHeader(int start, ushort size) public static implicit operator Range(SectionHeader section) => new(section.Start, section.EndOffset); } -public struct CompoundSectionHeader(int start, int[] subSectionsSizes) +public readonly struct CompoundSectionHeader(int start, int[] subSectionsSizes) { public readonly int Start => start; @@ -93,25 +92,20 @@ public struct CompoundSectionHeader(int start, int[] subSectionsSizes) public readonly int Size => EndOffset - Start; public readonly int Count => SubSectionsSizes.Length; - private int[] subSectionsSizesAcc; - private int[] SubSectionsSizesAcc + private static int[] CreateSubSectionsSizes(int[] subSectionsSizes) { - get + var subSectionsSizesAcc = new int[subSectionsSizes.Length]; + subSectionsSizesAcc[0] = 0; + for (var i = 1; i < subSectionsSizes.Length; i++) { - if (subSectionsSizesAcc is null) - { - 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; + subSectionsSizesAcc[i] = subSectionsSizesAcc[i - 1] + subSectionsSizes[i - 1]; } + + return subSectionsSizesAcc; } + private int[] SubSectionsSizesAcc { get; } = CreateSubSectionsSizes(subSectionsSizes); + public SectionHeader this[int i] => new SectionHeader(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 index 181430142df..4531eea20d7 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs @@ -1,14 +1,9 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Nethermind.Evm.EvmObjectFormat; -public enum ValidationStrategy + +public enum ValidationStrategy : byte { None = 0, Validate = 1, diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 75fcdaea0e2..a9112ab1965 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -2,36 +2,32 @@ // 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.Text; -using System.Threading.Tasks; -using Nethermind.Core.Extensions; -using Nethermind.Logging; -using Nethermind.Evm; -using System.Buffers; -using FastEnumUtility; using System.Runtime.InteropServices; using DotNetty.Common.Utilities; +using FastEnumUtility; +using Nethermind.Core.Extensions; +using Nethermind.Evm; + using static Nethermind.Evm.EvmObjectFormat.EofValidator; -using System.Reflection; namespace Nethermind.Evm.EvmObjectFormat.Handlers; internal class Eof1 : IEofVersionHandler { - struct QueueManager + private readonly struct QueueManager { - public Queue<(int index, ValidationStrategy strategy)> ContainerQueue; - public byte[] VisitedContainers; + public readonly Queue<(int index, ValidationStrategy strategy)> ContainerQueue; + public readonly ValidationStrategy[] VisitedContainers; public QueueManager(int containerCount) { ContainerQueue = new(); - VisitedContainers = new byte[containerCount]; - - VisitedContainers.Fill((byte)0); + VisitedContainers = new ValidationStrategy[containerCount]; } public void Enqueue(int index, ValidationStrategy strategy) @@ -39,9 +35,9 @@ public void Enqueue(int index, ValidationStrategy strategy) ContainerQueue.Enqueue((index, strategy)); } - public void MarkVisited(int index, byte strategy) + public void MarkVisited(int index, ValidationStrategy strategy) { - VisitedContainers[index] = (byte)strategy; + VisitedContainers[index] = strategy; } public bool TryDequeue(out (int Index, ValidationStrategy Strategy) worklet) => ContainerQueue.TryDequeue(out worklet); @@ -50,7 +46,7 @@ public void MarkVisited(int index, byte strategy) } [StructLayout(LayoutKind.Sequential)] - struct StackBounds() + private struct StackBounds() { public short Max = -1; public short Min = 1023; @@ -324,7 +320,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => return true; } - public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy validationStrategy, out EofContainer? eofContainer) + public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy validationStrategy, [NotNullWhen(true)] out EofContainer? eofContainer) { if (!TryParseEofHeader(code, out EofHeader? header)) { @@ -352,39 +348,73 @@ public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy val return true; } - public bool ValidateContainer(EofContainer eofContainer, ValidationStrategy validationStrategy) + private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy validationStrategy) { - QueueManager containerQueue = new(1 + (eofContainer.Header.ContainerSections?.Count ?? 0)); - containerQueue.Enqueue(0, validationStrategy); + Queue containers = new Queue(); + containers.Enqueue(eofContainer); - containerQueue.VisitedContainers[0] = validationStrategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) - ? (byte)ValidationStrategy.ValidateInitcodeMode - : validationStrategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) - ? (byte)ValidationStrategy.ValidateRuntimeMode - : (byte)0; - - while (containerQueue.TryDequeue(out var worklet)) + while (containers.TryDequeue(out EofContainer targetContainer)) { - EofContainer targetContainer = eofContainer; - if (worklet.Index != 0) + QueueManager containerQueue = new(1 + (targetContainer.Header.ContainerSections?.Count ?? 0)); + containerQueue.Enqueue(0, validationStrategy); + + containerQueue.VisitedContainers[0] = GetValidation(validationStrategy); + + while (containerQueue.TryDequeue(out var worklet)) { - if (containerQueue.VisitedContainers[worklet.Index] != 0) - continue; + if (worklet.Index != 0) + { + if (containerQueue.VisitedContainers[worklet.Index] != 0) + continue; - if (!TryGetEofContainer(targetContainer.ContainerSections[worklet.Index - 1], worklet.Strategy, out EofContainer? subContainer)) - return false; + if (targetContainer.CodeSections.Length < worklet.Index) + continue; + + ReadOnlyMemory subsection = targetContainer.CodeSections[worklet.Index - 1]; + if (!TryParseEofHeader(subsection, out EofHeader? header) || + !ValidateBody(subsection.Span, header.Value, validationStrategy)) + { + return false; + } + + if (validationStrategy.HasFlag(ValidationStrategy.Validate)) + { + containers.Enqueue(new EofContainer(subsection, header.Value)); + } + } + else + { + if (!ValidateCodeSections(targetContainer, worklet.Strategy, in containerQueue)) + return false; + } + containerQueue.MarkVisited(worklet.Index, GetVisited(worklet.Strategy)); } - else + if (!containerQueue.IsAllVisited()) { - if (!ValidateCodeSections(targetContainer, worklet.Strategy, containerQueue)) - return false; + return false; } - containerQueue.MarkVisited(worklet.Index, (byte)(worklet.Strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) ? ValidationStrategy.ValidateInitcodeMode : ValidationStrategy.ValidateRuntimeMode)); } - return containerQueue.IsAllVisited(); + + return true; + } + + private static ValidationStrategy GetVisited(ValidationStrategy validationStrategy) + { + return validationStrategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) + ? ValidationStrategy.ValidateInitcodeMode + : ValidationStrategy.ValidateRuntimeMode; + } + + private static ValidationStrategy GetValidation(ValidationStrategy validationStrategy) + { + return validationStrategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) + ? ValidationStrategy.ValidateInitcodeMode + : validationStrategy.HasFlag(ValidationStrategy.ValidateRuntimeMode) + ? ValidationStrategy.ValidateRuntimeMode + : ValidationStrategy.None; } - bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStrategy strategy) + private bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStrategy strategy) { int startOffset = header.TypeSection.Start; int endOffset = header.DataSection.Start; @@ -452,7 +482,7 @@ bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStra return true; } - bool ValidateCodeSections(EofContainer eofContainer, ValidationStrategy strategy, QueueManager containerQueue) + private bool ValidateCodeSections(EofContainer eofContainer, ValidationStrategy strategy, in QueueManager containerQueue) { QueueManager sectionQueue = new(eofContainer.Header.CodeSections.Count); @@ -463,16 +493,16 @@ bool ValidateCodeSections(EofContainer eofContainer, ValidationStrategy strategy if (sectionQueue.VisitedContainers[sectionIdx.Index] != 0) continue; - if (!ValidateInstructions(eofContainer, sectionIdx.Index, strategy, sectionQueue, containerQueue)) + if (!ValidateInstructions(eofContainer, sectionIdx.Index, strategy, in sectionQueue, in containerQueue)) return false; - sectionQueue.MarkVisited(sectionIdx.Index, 1); + sectionQueue.MarkVisited(sectionIdx.Index, ValidationStrategy.Validate); } return sectionQueue.IsAllVisited(); } - bool ValidateTypeSection(ReadOnlySpan types) + private bool ValidateTypeSection(ReadOnlySpan types) { if (types[INPUTS_OFFSET] != 0 || types[OUTPUTS_OFFSET] != NON_RETURNING) { @@ -513,7 +543,7 @@ bool ValidateTypeSection(ReadOnlySpan types) return true; } - bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationStrategy strategy, QueueManager sectionsWorklist, QueueManager containersWorklist) + private bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationStrategy strategy, in QueueManager sectionsWorklist, in QueueManager containersWorklist) { ReadOnlySpan code = eofContainer.CodeSections[sectionId].Span; @@ -548,14 +578,14 @@ bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationSt } else { - if (containersWorklist.VisitedContainers[0] == (byte)ValidationStrategy.ValidateInitcodeMode) + 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] = (byte)ValidationStrategy.ValidateRuntimeMode; + containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateRuntimeMode; } } } @@ -729,14 +759,14 @@ bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationSt } else { - if (containersWorklist.VisitedContainers[0] == (byte)ValidationStrategy.ValidateRuntimeMode) + if (containersWorklist.VisitedContainers[0] == ValidationStrategy.ValidateRuntimeMode) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); return false; } else { - containersWorklist.VisitedContainers[0] = (byte)ValidationStrategy.ValidateInitcodeMode; + containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateInitcodeMode; } } @@ -754,7 +784,7 @@ bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationSt } if (containersWorklist.VisitedContainers[runtimeContainerId + 1] != 0 - && containersWorklist.VisitedContainers[runtimeContainerId + 1] != (byte)ValidationStrategy.ValidateRuntimeMode) + && containersWorklist.VisitedContainers[runtimeContainerId + 1] != ValidationStrategy.ValidateRuntimeMode) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); return false; @@ -783,7 +813,7 @@ bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationSt } if (containersWorklist.VisitedContainers[initcodeSectionId + 1] != 0 - && containersWorklist.VisitedContainers[initcodeSectionId + 1] != (byte)ValidationStrategy.ValidateInitcodeMode) + && 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; From 39c6c7e593ddc1c234dbcc7a07f7a831f398edea Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 5 Sep 2024 08:56:31 +0100 Subject: [PATCH 141/255] Fix subsection init --- .../EvmObjectFormat/Handlers/EofV1.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index a9112ab1965..b205803b32d 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -350,11 +350,13 @@ public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy val private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy validationStrategy) { - Queue containers = new Queue(); - containers.Enqueue(eofContainer); - - while (containers.TryDequeue(out EofContainer targetContainer)) + Queue<(EofContainer container, ValidationStrategy strategy)> containers = new(); + containers.Enqueue((eofContainer, validationStrategy)); + while (containers.TryDequeue(out var target)) { + EofContainer targetContainer = target.container; + validationStrategy = target.strategy; + QueueManager containerQueue = new(1 + (targetContainer.Header.ContainerSections?.Count ?? 0)); containerQueue.Enqueue(0, validationStrategy); @@ -367,10 +369,10 @@ private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy val if (containerQueue.VisitedContainers[worklet.Index] != 0) continue; - if (targetContainer.CodeSections.Length < worklet.Index) + if (targetContainer.ContainerSections.Length < worklet.Index) continue; - ReadOnlyMemory subsection = targetContainer.CodeSections[worklet.Index - 1]; + ReadOnlyMemory subsection = targetContainer.ContainerSections[worklet.Index - 1]; if (!TryParseEofHeader(subsection, out EofHeader? header) || !ValidateBody(subsection.Span, header.Value, validationStrategy)) { @@ -379,7 +381,11 @@ private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy val if (validationStrategy.HasFlag(ValidationStrategy.Validate)) { - containers.Enqueue(new EofContainer(subsection, header.Value)); + // Clear the Initcode flag for subcontainer + ValidationStrategy subContainerValidation = validationStrategy & ~ValidationStrategy.ValidateInitcodeMode; + // Set the Runtime flag for subcontainer + subContainerValidation |= ValidationStrategy.ValidateRuntimeMode; + containers.Enqueue((new EofContainer(subsection, header.Value), subContainerValidation)); } } else From 970a4f3905e63c973e9ae4b3a38b872a7daba799 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 9 Sep 2024 08:44:36 +0100 Subject: [PATCH 142/255] Update tests --- .../Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs index 904c442a414..7e83504cd00 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.8" + ArchiveVersion = "eip7692@v1.0.9" }, $"fixtures/blockchain_tests/prague"); return loader.LoadTests().Cast(); } diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs index cc62ab55a43..8792b002f98 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs @@ -20,7 +20,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.8" + ArchiveVersion = "eip7692@v1.0.9" }, $"fixtures/eof_tests/prague"); return loader.LoadTests().Cast(); } diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index 2a8996eaae9..0f3a8acbfc7 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.8" + ArchiveVersion = "eip7692@v1.0.9" }, $"fixtures/state_tests/prague"); return loader.LoadTests().Cast(); } From 35d724e7cdaee6b45f01039aaa6cf858db33d0c6 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 9 Sep 2024 14:36:28 -0600 Subject: [PATCH 143/255] ssert tests --- .../Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs index 8792b002f98..1d0601fb603 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs @@ -13,7 +13,7 @@ namespace Ethereum.Blockchain.Pyspec.Test; public class PragueEofTests : EofTestBase { [TestCaseSource(nameof(LoadTests))] - public void Test(EofTest test) => RunTest(test); + public void Test(EofTest test) => Assert.That(RunTest(test)); private static IEnumerable LoadTests() { From 42f502af6ea8b615f733efbd79fbd5950945d0d8 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 10 Sep 2024 00:29:11 -0600 Subject: [PATCH 144/255] Eof/valdiation fixes (#7405) Co-authored-by: Ben Adams --- .../EvmObjectFormat/EofCodeValidator.cs | 2 +- .../EvmObjectFormat/Handlers/EofV1.cs | 79 +++++++++++++++++-- .../EvmObjectFormat/IEofVersionHandler.cs | 2 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 5 +- 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 06185b7acc0..ebcc7f67c6c 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -52,7 +52,7 @@ public static bool IsValidEofHeader(ReadOnlyMemory code, [NotNullWhen(true { if (IsEof(code, out byte version) && _eofVersionHandlers.TryGetValue(version, out IEofVersionHandler handler)) { - return handler.TryParseEofHeader(code, out header); + return handler.TryParseEofHeader(code, ValidationStrategy.Validate, out header); } header = null; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index b205803b32d..06d9db617af 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -118,7 +118,10 @@ internal enum Separator : byte + MINIMUM_CODESECTION_SIZE // minimum code section body size + MINIMUM_DATASECTION_SIZE; // minimum data section body size - public bool TryParseEofHeader(ReadOnlyMemory containerMemory, out EofHeader? header) + // EIP-3540 ties this to MAX_INIT_CODE_SIZE from EIP-3860, but we need a constant here + internal const ushort MAXIMUM_SIZE = 0xc000; + + public bool TryParseEofHeader(ReadOnlyMemory containerMemory, ValidationStrategy validationStrategy, out EofHeader? header) { [MethodImpl(MethodImplOptions.AggressiveInlining)] static ushort GetUInt16(ReadOnlySpan container, int offset) => @@ -133,6 +136,11 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => 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(EofValidator.MAGIC)) { @@ -159,6 +167,12 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => switch (separator) { case Separator.KIND_TYPE: + if (sectionSizes.TypeSectionSize != null) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Multiple type sections"); + return false; + } + if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); @@ -175,6 +189,12 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => pos += EofValidator.TWO_BYTE_LENGTH; 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 fromated"); @@ -220,12 +240,24 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => pos += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfCodeSections; 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 fromated"); return false; } + if (sectionSizes.DataSectionSize is not null) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Container section is out of order"); + return false; + } + if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); @@ -265,6 +297,12 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => pos += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfContainerSections; 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 fromated"); @@ -308,6 +346,18 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => : new CompoundSectionHeader(codeSectionSubHeader.EndOffset, containerSections); var dataSectionSubHeader = new SectionHeader(containerSectionSubHeader?.EndOffset ?? codeSectionSubHeader.EndOffset, sectionSizes.DataSectionSize.Value); + if (dataSectionSubHeader.EndOffset < containerMemory.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Extra data after end of container, starting at {dataSectionSubHeader.EndOffset}"); + return false; + } + if ((validationStrategy.HasFlag(ValidationStrategy.Validate) && !validationStrategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) + && dataSectionSubHeader.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, @@ -322,7 +372,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy validationStrategy, [NotNullWhen(true)] out EofContainer? eofContainer) { - if (!TryParseEofHeader(code, out EofHeader? header)) + if (!TryParseEofHeader(code, validationStrategy, out EofHeader? header)) { eofContainer = null; return false; @@ -373,7 +423,7 @@ private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy val continue; ReadOnlyMemory subsection = targetContainer.ContainerSections[worklet.Index - 1]; - if (!TryParseEofHeader(subsection, out EofHeader? header) || + if (!TryParseEofHeader(subsection, validationStrategy, out EofHeader? header) || !ValidateBody(subsection.Span, header.Value, validationStrategy)) { return false; @@ -553,6 +603,12 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali { ReadOnlySpan code = eofContainer.CodeSections[sectionId].Span; + if (code.Length < 1) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection {sectionId} is too short to be valid"); + return false; + } + var length = code.Length / BYTE_BIT_COUNT + 1; byte[] codeBitmapArray = ArrayPool.Shared.Rent(length); byte[] jumpDestsArray = ArrayPool.Shared.Rent(length); @@ -570,9 +626,10 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali var isCurrentSectionNonReturning = currentTypesection[OUTPUTS_OFFSET] == 0x80; int pos; + Instruction opcode = Instruction.STOP; for (pos = 0; pos < code.Length;) { - var opcode = (Instruction)code[pos]; + opcode = (Instruction)code[pos]; var postInstructionByte = pos + 1; if (opcode is Instruction.RETURN or Instruction.STOP) @@ -810,7 +867,6 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } var initcodeSectionId = code[postInstructionByte]; - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); if (eofContainer.Header.ContainerSections is null || initcodeSectionId >= eofContainer.Header.ContainerSections?.Count) { @@ -849,6 +905,12 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } + if (!opcode.IsTerminating()) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code section {sectionId} ends with a non-terminating opcode"); + return false; + } + var result = !BitmapHelper.CheckCollision(codeBitmap, jumpDests); if (!result) if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination"); @@ -1019,7 +1081,14 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea if (opcode.IsTerminating()) { if (programCounter < code.Length) + { + if (recordedStackHeight[programCounter].Max < 0) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, opcode not forward referenced, section {sectionId} pc {programCounter}"); + return false; + } currentStackBounds = recordedStackHeight[programCounter]; + } } else { diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs index 407a97dfc3b..7254ced54ea 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs @@ -7,6 +7,6 @@ namespace Nethermind.Evm.EvmObjectFormat; interface IEofVersionHandler { - bool TryParseEofHeader(ReadOnlyMemory code, [NotNullWhen(true)] out EofHeader? header); + bool TryParseEofHeader(ReadOnlyMemory code, ValidationStrategy strategy, [NotNullWhen(true)] out EofHeader? header); bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy strategy, [NotNullWhen(true)] out EofContainer? header); } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 6e17f9ede38..3380e00e2ce 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -195,7 +195,7 @@ public enum Instruction : byte EXCHANGE = 0xe8, // random value opcode spec has collision RETURNDATALOAD = 0xf7, - // opcode value not spec-ed + // opcode value not spec-ed EXTCALL = 0xf8, EXTDELEGATECALL = 0xf9, // DelegateCallEnabled EXTSTATICCALL = 0xfb, // StaticCallEnabled @@ -240,6 +240,7 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) Instruction.CALL => !IsEofContext, Instruction.CALLCODE => !IsEofContext, Instruction.DELEGATECALL => !IsEofContext, + Instruction.STATICCALL => !IsEofContext, Instruction.SELFDESTRUCT => !IsEofContext, Instruction.JUMP => !IsEofContext, Instruction.JUMPI => !IsEofContext, @@ -255,7 +256,7 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) }; } - //Note() : Extensively test this, refactor it, + //Note() : Extensively test this, refactor it, public static (ushort? InputCount, ushort? OutputCount, ushort? immediates) StackRequirements(this Instruction instruction) => instruction switch { Instruction.STOP => (0, 0, 0), From d4980f4933fddcaab759791e052288eb43776134 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 17 Sep 2024 19:57:47 +0100 Subject: [PATCH 145/255] Break tests up --- .../LoadPyspecTestsStrategy.cs | 2 +- .../PragueEofTests.cs | 6 ++- src/Nethermind/Ethereum.Test.Base/EofTest.cs | 5 +- .../Ethereum.Test.Base/EofTestBase.cs | 47 ++++++++----------- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 33 ++++++++----- .../EvmObjectFormat/EofCodeValidator.cs | 1 + 6 files changed, 50 insertions(+), 44 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/LoadPyspecTestsStrategy.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/LoadPyspecTestsStrategy.cs index 705c06f751c..f363f20e2dc 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/LoadPyspecTestsStrategy.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/LoadPyspecTestsStrategy.cs @@ -74,7 +74,7 @@ private IEnumerable LoadTestsFromDirectory(string testDir, string foreach (IEthereumTest test in tests) { - test.Category = testDir; + test.Category ??= testDir; } testsByName.AddRange(tests); } diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs index 1d0601fb603..9aff570bfb3 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs @@ -15,13 +15,15 @@ public class PragueEofTests : EofTestBase [TestCaseSource(nameof(LoadTests))] public void Test(EofTest test) => Assert.That(RunTest(test)); - private static IEnumerable LoadTests() + private static IEnumerable LoadTests() { TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", ArchiveVersion = "eip7692@v1.0.9" }, $"fixtures/eof_tests/prague"); - return loader.LoadTests().Cast(); + return loader.LoadTests().Cast().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 index 591a7edf435..8176268bd7f 100644 --- a/src/Nethermind/Ethereum.Test.Base/EofTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/EofTest.cs @@ -8,6 +8,7 @@ namespace Ethereum.Test.Base; public class Result { + public string Fork { get; set; } public bool Success { get; set; } public string? Error { get; set; } } @@ -15,13 +16,13 @@ public class Result public class VectorTest { public byte[] Code { get; set; } - public Dictionary Results { get; set; } } public class EofTest : IEthereumTest { public string Name { get; set; } - public VectorTest[] Vectors { get; set; } + public VectorTest Vector { get; set; } public string? Category { get; set; } public string? LoadFailure { get; set; } + public Result Result { get; internal set; } } diff --git a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs index f7752af46a0..8fdb20cd352 100644 --- a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs @@ -27,6 +27,7 @@ using NUnit.Framework; using System.Threading.Tasks; using Nethermind.TxPool; +using Nethermind.Evm.EvmObjectFormat; namespace Ethereum.Test.Base { @@ -38,6 +39,7 @@ public abstract class EofTestBase [SetUp] public void Setup() { + EofValidator.Logger = _logger; } protected static void Setup(ILogManager logManager) @@ -53,38 +55,29 @@ protected bool RunTest(EofTest test) protected bool RunTest(EofTest test, ITxTracer txTracer) { - TestContext.Write($"Running {test.Name} at {DateTime.UtcNow:HH:mm:ss.ffffff}"); + TestContext.WriteLine($"Running {test.Name} at {DateTime.UtcNow:HH:mm:ss.ffffff}"); Assert.IsNull(test.LoadFailure, "test data loading failure"); - - List results = new(); - foreach (var vector in test.Vectors) + var vector = test.Vector; + var code = vector.Code; + var fork = test.Result.Fork switch { - var code = vector.Code; - foreach (var kvp in vector.Results) - { - var fork = kvp.Key switch - { - "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 {kvp.Key} is not supported") - }; - - bool result = CodeDepositHandler.IsValidWithEofRules(fork, code, 1); - results.Add(result == kvp.Value.Success); - } + "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") + }; - } + bool result = CodeDepositHandler.IsValidWithEofRules(fork, code, 1); - return results.TrueForAll(r => r); + return result == test.Result.Success; } } } diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index df0372649bd..60afd949743 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -289,22 +289,31 @@ public static IEnumerable ConvertToEofTests(string json) List tests = new(); foreach (KeyValuePair namedTest in testsInFile) { - EofTest test = new(); - test.Name = namedTest.Key; - test.Vectors = namedTest.Value.Vectors.Select(pair => + var index = namedTest.Key.IndexOf(".py::"); + var name = namedTest.Key.Substring(index+5); + string category = namedTest.Key.Substring(0, index).Replace("tests/prague/eip7692_eof_v1/", ""); + + foreach (KeyValuePair pair in namedTest.Value.Vectors) { VectorTestJson vectorJson = pair.Value; VectorTest vector = new(); vector.Code = Bytes.FromHexString(vectorJson.Code); - vector.Results = vectorJson.Results.ToDictionary( - p => p.Key, - p => p.Value.Result - ? new Result { Success = true } - : new Result { Success = false, Error = p.Value.Exception } - ); - return vector; - }).ToArray(); - tests.Add(test); + + foreach (var result in vectorJson.Results) + { + EofTest test = new() + { + Name = $"{name}", + Category = $"{category} [{result.Key}]" + }; + test.Vector = vector; + + test.Result = result.Value.Result + ? new Result { Fork = result.Key, Success = true } + : new Result { Fork = result.Key, Success = false, Error = result.Value.Exception }; + tests.Add(test); + } + } } return tests; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index ebcc7f67c6c..b484d554750 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -9,6 +9,7 @@ using Nethermind.Logging; [assembly: InternalsVisibleTo("Nethermind.EofParser")] +[assembly: InternalsVisibleTo("Ethereum.Test.Base")] namespace Nethermind.Evm.EvmObjectFormat; From b02e797b8407f4ed5b4c3bfc18f804fd43f18598 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 17 Sep 2024 20:00:51 +0100 Subject: [PATCH 146/255] Formatting --- src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 60afd949743..037d6ce0821 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -290,7 +290,7 @@ public static IEnumerable ConvertToEofTests(string json) foreach (KeyValuePair namedTest in testsInFile) { var index = namedTest.Key.IndexOf(".py::"); - var name = namedTest.Key.Substring(index+5); + var name = namedTest.Key.Substring(index + 5); string category = namedTest.Key.Substring(0, index).Replace("tests/prague/eip7692_eof_v1/", ""); foreach (KeyValuePair pair in namedTest.Value.Vectors) From 4d8ac025b9f95b1e0b8327b4c2a0694b71f7d0f1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 18 Sep 2024 09:31:55 +0100 Subject: [PATCH 147/255] Clarify tests --- .../PragueEofTests.cs | 2 +- .../Ethereum.Test.Base/EofTestBase.cs | 33 ++--- .../Ethereum.Test.Base/TextContextLogger.cs | 33 +++++ .../EvmObjectFormat/EofCodeValidator.cs | 4 + .../EvmObjectFormat/Handlers/EofV1.cs | 124 +++++++++--------- .../ReadOnlyMemoryExtensions.cs | 2 +- 6 files changed, 110 insertions(+), 88 deletions(-) create mode 100644 src/Nethermind/Ethereum.Test.Base/TextContextLogger.cs diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs index 9aff570bfb3..56b21a193fd 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs @@ -13,7 +13,7 @@ namespace Ethereum.Blockchain.Pyspec.Test; public class PragueEofTests : EofTestBase { [TestCaseSource(nameof(LoadTests))] - public void Test(EofTest test) => Assert.That(RunTest(test)); + public void Test(EofTest test) => RunCITest(test); private static IEnumerable LoadTests() { diff --git a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs index 8fdb20cd352..9f7936c8c36 100644 --- a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs @@ -2,38 +2,17 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; -using System.Diagnostics; -using Nethermind.Blockchain; -using Nethermind.Consensus.Ethash; -using Nethermind.Consensus.Validators; -using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; -using Nethermind.Core.Specs; -using Nethermind.Core.Test.Builders; -using Nethermind.Crypto; -using Nethermind.Db; -using Nethermind.Int256; using Nethermind.Evm; using Nethermind.Evm.Tracing; -using Nethermind.Evm.TransactionProcessing; using Nethermind.Logging; -using Nethermind.Specs; -using Nethermind.Specs.Forks; -using Nethermind.Specs.Test; -using Nethermind.State; -using Nethermind.Trie.Pruning; using NUnit.Framework; -using System.Threading.Tasks; -using Nethermind.TxPool; using Nethermind.Evm.EvmObjectFormat; namespace Ethereum.Test.Base { public abstract class EofTestBase { - private static ILogger _logger = new(new ConsoleAsyncLogger(LogLevel.Info)); + private static ILogger _logger = new(TextContextLogger.Instance); private static ILogManager _logManager = new TestLogManager(LogLevel.Warn); [SetUp] @@ -48,9 +27,14 @@ protected static void Setup(ILogManager logManager) _logger = _logManager.GetClassLogger(); } + protected void RunCITest(EofTest test) + { + Assert.That(RunTest(test, NullTxTracer.Instance), Is.EqualTo(test.Result.Success)); + } + protected bool RunTest(EofTest test) { - return RunTest(test, NullTxTracer.Instance); + return RunTest(test, NullTxTracer.Instance) == test.Result.Success; } protected bool RunTest(EofTest test, ITxTracer txTracer) @@ -76,8 +60,7 @@ protected bool RunTest(EofTest test, ITxTracer txTracer) }; bool result = CodeDepositHandler.IsValidWithEofRules(fork, code, 1); - - return result == test.Result.Success; + return result; } } } diff --git a/src/Nethermind/Ethereum.Test.Base/TextContextLogger.cs b/src/Nethermind/Ethereum.Test.Base/TextContextLogger.cs new file mode 100644 index 00000000000..21bf4184378 --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/TextContextLogger.cs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Logging; +using NUnit.Framework; + +namespace Ethereum.Test.Base; + +public class TextContextLogger : InterfaceLogger +{ + private TextContextLogger() { } + + public static TextContextLogger Instance { get; } = new TextContextLogger(); + + public void Info(string text) => WriteEntry(text); + + public void Warn(string text) => WriteEntry(text); + + public void Debug(string text) => WriteEntry(text); + + public void Trace(string text) => WriteEntry(text); + + public void Error(string text, Exception ex = null) => WriteEntry(text + " " + ex); + + private static void WriteEntry(string text) => TestContext.WriteLine(text); + + public bool IsInfo => true; + public bool IsWarn => true; + public bool IsDebug => true; + public bool IsTrace => true; + public bool IsError => true; +} diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index b484d554750..17ebd80b37b 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -56,6 +56,7 @@ public static bool IsValidEofHeader(ReadOnlyMemory code, [NotNullWhen(true return handler.TryParseEofHeader(code, ValidationStrategy.Validate, out header); } + if (Logger.IsTrace) Logger.Trace($"EOF: Eof not recognized"); header = null; return false; } @@ -64,12 +65,14 @@ public static bool IsValidEof(ReadOnlyMemory code, ValidationStrategy stra { if (strategy == ValidationStrategy.None) { + if (Logger.IsTrace) Logger.Trace($"EOF: No validation"); eofContainer = null; return true; } if (strategy.HasFlag(ValidationStrategy.HasEofMagic) && !code.StartsWith(MAGIC)) { + if (Logger.IsTrace) Logger.Trace($"EOF: No MAGIC as start of code"); eofContainer = null; return false; } @@ -79,6 +82,7 @@ public static bool IsValidEof(ReadOnlyMemory code, ValidationStrategy stra return handler.TryGetEofContainer(code, strategy, out eofContainer); } + if (Logger.IsTrace) Logger.Trace($"EOF: Not EOF"); eofContainer = null; return false; } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 06d9db617af..b9f06e4461f 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -130,7 +130,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => ReadOnlySpan container = containerMemory.Span; header = null; - // we need to be able to parse header + minimum section lenghts + // we need to be able to parse header + minimum section lengths if (container.Length < MINIMUM_SIZE) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); @@ -197,7 +197,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => if (sectionSizes.TypeSectionSize is null) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); return false; } @@ -223,7 +223,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => codeSections = new int[numberOfCodeSections]; int CODESECTION_HEADER_PREFIX_SIZE = pos + EofValidator.TWO_BYTE_LENGTH; - for (ushort i = 0; i < numberOfCodeSections; i++) + for (ushort i = 0; i < codeSections.Length; i++) { int currentCodeSizeOffset = CODESECTION_HEADER_PREFIX_SIZE + i * EofValidator.TWO_BYTE_LENGTH; // offset of pos'th code size int codeSectionSize = GetUInt16(container, currentCodeSizeOffset); @@ -237,7 +237,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => codeSections[i] = codeSectionSize; } - pos += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfCodeSections; + pos += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * codeSections.Length; break; case Separator.KIND_CONTAINER: if (sectionSizes.ContainerSectionSize != null) @@ -248,7 +248,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => if (sectionSizes.CodeSectionSize is null) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well fromated"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); return false; } @@ -268,7 +268,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * EofValidator.TWO_BYTE_LENGTH); if (numberOfContainerSections is > MAXIMUM_NUM_CONTAINER_SECTIONS or 0) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, code sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, container sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); return false; } @@ -280,7 +280,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => containerSections = new int[numberOfContainerSections]; int CONTAINER_SECTION_HEADER_PREFIX_SIZE = pos + EofValidator.TWO_BYTE_LENGTH; - for (ushort i = 0; i < numberOfContainerSections; i++) + for (ushort i = 0; i < containerSections.Length; i++) { int currentContainerSizeOffset = CONTAINER_SECTION_HEADER_PREFIX_SIZE + i * EofValidator.TWO_BYTE_LENGTH; // offset of pos'th code size int containerSectionSize = GetUInt16(container, currentContainerSizeOffset); @@ -294,7 +294,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => containerSections[i] = containerSectionSize; } - pos += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfContainerSections; + pos += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * containerSections.Length; break; case Separator.KIND_DATA: if (sectionSizes.DataSectionSize != null) @@ -374,12 +374,14 @@ public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy val { if (!TryParseEofHeader(code, validationStrategy, out EofHeader? header)) { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Header not parsed"); eofContainer = null; return false; } if (!ValidateBody(code.Span, header.Value, validationStrategy)) { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Body not valid"); eofContainer = null; return false; } @@ -390,6 +392,7 @@ public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy val { if (!ValidateContainer(eofContainer.Value, validationStrategy)) { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Container not valid"); eofContainer = null; return false; } @@ -422,10 +425,16 @@ private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy val if (targetContainer.ContainerSections.Length < worklet.Index) continue; - ReadOnlyMemory subsection = targetContainer.ContainerSections[worklet.Index - 1]; - if (!TryParseEofHeader(subsection, validationStrategy, out EofHeader? header) || - !ValidateBody(subsection.Span, header.Value, validationStrategy)) + var section = worklet.Index - 1; + ReadOnlyMemory subsection = targetContainer.ContainerSections[section]; + if (!TryParseEofHeader(subsection, validationStrategy, out EofHeader? header)) { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Header invalid: section {section}"); + return false; + } + if (!ValidateBody(subsection.Span, header.Value, validationStrategy)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Body invalid: section {section}"); return false; } @@ -441,12 +450,16 @@ private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy val else { if (!ValidateCodeSections(targetContainer, worklet.Strategy, in containerQueue)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code sections invalid"); return false; + } } containerQueue.MarkVisited(worklet.Index, GetVisited(worklet.Strategy)); } if (!containerQueue.IsAllVisited()) { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Not all containers visited"); return false; } } @@ -610,20 +623,20 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } var length = code.Length / BYTE_BIT_COUNT + 1; - byte[] codeBitmapArray = ArrayPool.Shared.Rent(length); - byte[] jumpDestsArray = ArrayPool.Shared.Rent(length); + byte[] dataBitmapArray = ArrayPool.Shared.Rent(length); + byte[] jumpDestArray = ArrayPool.Shared.Rent(length); try { // ArrayPool may return a larger array than requested, so we need to slice it to the actual length - Span codeBitmap = codeBitmapArray.AsSpan(0, length); - Span jumpDests = jumpDestsArray.AsSpan(0, length); + Span dataBitmap = dataBitmapArray.AsSpan(0, length); + Span jumpDest = jumpDestArray.AsSpan(0, length); // ArrayPool may return a larger array than requested, so we need to slice it to the actual length - codeBitmap.Clear(); - jumpDests.Clear(); + dataBitmap.Clear(); + jumpDest.Clear(); - ReadOnlySpan currentTypesection = eofContainer.TypeSections[sectionId].Span; - var isCurrentSectionNonReturning = currentTypesection[OUTPUTS_OFFSET] == 0x80; + ReadOnlySpan currentTypeSection = eofContainer.TypeSections[sectionId].Span; + var isCurrentSectionNonReturning = currentTypeSection[OUTPUTS_OFFSET] == 0x80; int pos; Instruction opcode = Instruction.STOP; @@ -632,7 +645,12 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali opcode = (Instruction)code[pos]; var postInstructionByte = pos + 1; - if (opcode is Instruction.RETURN or Instruction.STOP) + 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) { if (strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode)) { @@ -652,14 +670,7 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } } } - - if (!opcode.IsValid(IsEofContext: true)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains undefined opcode {opcode}"); - return false; - } - - if (opcode is Instruction.RJUMP or Instruction.RJUMPI) + else if (opcode is Instruction.RJUMP or Instruction.RJUMPI) { if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) { @@ -676,11 +687,10 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDests, ref rjumpdest); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDest, ref rjumpdest); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, dataBitmap, ref pos); } - - if (opcode is Instruction.JUMPF) + else if (opcode is Instruction.JUMPF) { if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) { @@ -700,7 +710,7 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali var targetSectionOutputCount = targetTypesection[OUTPUTS_OFFSET]; var isTargetSectionNonReturning = targetTypesection[OUTPUTS_OFFSET] == 0x80; - var currentSectionOutputCount = currentTypesection[OUTPUTS_OFFSET]; + var currentSectionOutputCount = currentTypeSection[OUTPUTS_OFFSET]; if (!isTargetSectionNonReturning && currentSectionOutputCount < targetSectionOutputCount) { @@ -709,21 +719,19 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, dataBitmap, ref postInstructionByte); } - - if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) + else if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) { if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); return false; } - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, dataBitmap, ref postInstructionByte); } - - if (opcode is Instruction.RJUMPV) + else if (opcode is Instruction.RJUMPV) { if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH > code.Length) { @@ -754,12 +762,11 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Destination outside of Code bounds"); return false; } - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDests, ref rjumpdest); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDest, ref rjumpdest); } - BitmapHelper.HandleNumbits(immediateValueSize, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(immediateValueSize, dataBitmap, ref postInstructionByte); } - - if (opcode is Instruction.CALLF) + else if (opcode is Instruction.CALLF) { if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) { @@ -786,16 +793,14 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, dataBitmap, ref postInstructionByte); } - - if (opcode is Instruction.RETF && isCurrentSectionNonReturning) + else if (opcode is Instruction.RETF && isCurrentSectionNonReturning) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, non returning sections are not allowed to use opcode {Instruction.RETF}"); return false; } - - if (opcode is Instruction.DATALOADN) + else if (opcode is Instruction.DATALOADN) { if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) { @@ -810,10 +815,9 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali 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.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, dataBitmap, ref postInstructionByte); } - - if (opcode is Instruction.RETURNCONTRACT) + else if (opcode is Instruction.RETURNCONTRACT) { if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) { @@ -855,10 +859,9 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode); - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, dataBitmap, ref postInstructionByte); } - - if (opcode is Instruction.EOFCREATE) + else if (opcode is Instruction.EOFCREATE) { if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH > code.Length) { @@ -883,10 +886,9 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali containersWorklist.Enqueue(initcodeSectionId + 1, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateFullBody); - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, dataBitmap, ref postInstructionByte); } - - if (opcode is >= Instruction.PUSH0 and <= Instruction.PUSH32) + else if (opcode is >= Instruction.PUSH0 and <= Instruction.PUSH32) { int len = opcode - Instruction.PUSH0; if (postInstructionByte + len > code.Length) @@ -894,7 +896,7 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} PC Reached out of bounds"); return false; } - BitmapHelper.HandleNumbits(len, codeBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(len, dataBitmap, ref postInstructionByte); } pos = postInstructionByte; } @@ -911,7 +913,7 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } - var result = !BitmapHelper.CheckCollision(codeBitmap, jumpDests); + var result = !BitmapHelper.CheckCollision(dataBitmap, jumpDest); if (!result) if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination"); @@ -921,8 +923,8 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } finally { - ArrayPool.Shared.Return(codeBitmapArray); - ArrayPool.Shared.Return(jumpDestsArray); + ArrayPool.Shared.Return(dataBitmapArray); + ArrayPool.Shared.Return(jumpDestArray); } } public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection) diff --git a/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs b/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs index b36a8ce9ac0..79a9ab6b584 100644 --- a/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/ReadOnlyMemoryExtensions.cs @@ -20,7 +20,7 @@ public static bool StartsWith(this ReadOnlyMemory inputData, Span st public static byte ByteAt(this ReadOnlyMemory inputData, int index) { - return inputData.Span[index]; + return inputData.Length > index ? inputData.Span[index] : (byte)0; } } } From 1b7df28ad4421a933f7a895d1f760fc6cb6722de Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 18 Sep 2024 11:22:05 +0100 Subject: [PATCH 148/255] Fix bitmaps --- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 31 ++-- .../EvmObjectFormat/Handlers/EofV1.cs | 141 +++++++++--------- 2 files changed, 94 insertions(+), 78 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index c9f62ac9ded..a1df6e39dbd 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -2,13 +2,24 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; namespace Nethermind.Evm; public static class BitmapHelper { - private static readonly byte[] _lookup = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; + private static readonly byte[] _lookup = + { + 0b0000_0000, + 0b0000_0001, + 0b0000_0011, + 0b0000_0111, + 0b0000_1111, + 0b0001_1111, + 0b0011_1111, + 0b0111_1111 + }; /// /// Collects data locations in code. @@ -56,11 +67,9 @@ public static void HandleNumbits(int numbits, Span bitvec, scoped ref int } } - - ushort setNBitsMask = (ushort)(~((1 << 32 - numbits) - 1)); if (numbits > 1) { - bitvec.SetN(pc, setNBitsMask); + bitvec.SetN(pc, _lookup[numbits]); pc += numbits; } else @@ -79,14 +88,14 @@ public static bool IsCodeSegment(Span bitvec, int pos) private static void Set1(this Span bitvec, int pos) { - bitvec[pos / 8] |= _lookup[pos % 8]; + bitvec[pos / 8] |= (byte)(1 << (pos % 8)); } - private static void SetN(this Span bitvec, int pos, UInt16 flag) + private static void SetN(this Span bitvec, int pos, ushort flag) { - ushort a = (ushort)(flag >> (pos % 8)); - bitvec[pos / 8] |= (byte)(a >> 8); - byte b = (byte)a; + ushort a = (ushort)(flag << (pos % 8)); + bitvec[pos / 8] |= (byte)a; + byte b = (byte)(a >> 8); if (b != 0) { // If the bit-setting affects the neighbouring byte, we can assign - no need to OR it, @@ -97,14 +106,14 @@ private static void SetN(this Span bitvec, int pos, UInt16 flag) private static void Set8(this Span bitvec, int pos) { - byte a = (byte)(0xFF >> (pos % 8)); + byte a = (byte)(0xFF << (pos % 8)); bitvec[pos / 8] |= a; bitvec[pos / 8 + 1] = (byte)~a; } private static void Set16(this Span bitvec, int pos) { - byte a = (byte)(0xFF >> (pos % 8)); + byte a = (byte)(0xFF << (pos % 8)); bitvec[pos / 8] |= a; bitvec[pos / 8 + 1] = 0xFF; bitvec[pos / 8 + 2] = (byte)~a; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index b9f06e4461f..012a0d08c12 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -623,27 +623,27 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } var length = code.Length / BYTE_BIT_COUNT + 1; - byte[] dataBitmapArray = ArrayPool.Shared.Rent(length); - byte[] jumpDestArray = ArrayPool.Shared.Rent(length); + byte[] invalidJmpLocationArray = ArrayPool.Shared.Rent(length); + byte[] jumpDestinationsArray = ArrayPool.Shared.Rent(length); try { // ArrayPool may return a larger array than requested, so we need to slice it to the actual length - Span dataBitmap = dataBitmapArray.AsSpan(0, length); - Span jumpDest = jumpDestArray.AsSpan(0, length); + Span invalidJumpDestinations = invalidJmpLocationArray.AsSpan(0, length); + Span jumpDestinations = jumpDestinationsArray.AsSpan(0, length); // ArrayPool may return a larger array than requested, so we need to slice it to the actual length - dataBitmap.Clear(); - jumpDest.Clear(); + invalidJumpDestinations.Clear(); + jumpDestinations.Clear(); ReadOnlySpan currentTypeSection = eofContainer.TypeSections[sectionId].Span; var isCurrentSectionNonReturning = currentTypeSection[OUTPUTS_OFFSET] == 0x80; - int pos; + int position; Instruction opcode = Instruction.STOP; - for (pos = 0; pos < code.Length;) + for (position = 0; position < code.Length;) { - opcode = (Instruction)code[pos]; - var postInstructionByte = pos + 1; + opcode = (Instruction)code[position]; + int nextPosition = position + 1; if (!opcode.IsValid(IsEofContext: true)) { @@ -672,33 +672,33 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } else if (opcode is Instruction.RJUMP or Instruction.RJUMPI) { - if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); return false; } - var offset = code.Slice(postInstructionByte, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); - var rjumpdest = offset + EofValidator.TWO_BYTE_LENGTH + postInstructionByte; + short offset = code.Slice(nextPosition, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + int rjumpDest = offset + EofValidator.TWO_BYTE_LENGTH + nextPosition; - if (rjumpdest < 0 || rjumpdest >= code.Length) + if (rjumpDest < 0 || rjumpDest >= code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Destination outside of Code bounds"); return false; } - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDest, ref rjumpdest); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, dataBitmap, ref pos); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDestinations, ref rjumpDest); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.JUMPF) { - if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} Argument underflow"); return false; } - var targetSectionId = code.Slice(postInstructionByte, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + var targetSectionId = code.Slice(nextPosition, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); if (targetSectionId >= eofContainer.Header.CodeSections.Count) { @@ -706,10 +706,10 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } - ReadOnlySpan targetTypesection = eofContainer.TypeSections[targetSectionId].Span; + ReadOnlySpan targetTypeSection = eofContainer.TypeSections[targetSectionId].Span; - var targetSectionOutputCount = targetTypesection[OUTPUTS_OFFSET]; - var isTargetSectionNonReturning = targetTypesection[OUTPUTS_OFFSET] == 0x80; + var targetSectionOutputCount = targetTypeSection[OUTPUTS_OFFSET]; + var isTargetSectionNonReturning = targetTypeSection[OUTPUTS_OFFSET] == 0x80; var currentSectionOutputCount = currentTypeSection[OUTPUTS_OFFSET]; if (!isTargetSectionNonReturning && currentSectionOutputCount < targetSectionOutputCount) @@ -719,62 +719,63 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, dataBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) { - if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); return false; } - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, dataBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.RJUMPV) { - if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.ONE_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Argument underflow"); return false; } - var count = (ushort)(code[postInstructionByte] + 1); + var count = (ushort)(code[nextPosition] + 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"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumpTable must have at least 1 entry"); return false; } - if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH + count * EofValidator.TWO_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.ONE_BYTE_LENGTH + count * EofValidator.TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumptable underflow"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumpTable underflow"); return false; } - var immediateValueSize = EofValidator.ONE_BYTE_LENGTH + count * EofValidator.TWO_BYTE_LENGTH; + int immediateValueSize = EofValidator.ONE_BYTE_LENGTH + count * EofValidator.TWO_BYTE_LENGTH; for (var j = 0; j < count; j++) { - var offset = code.Slice(postInstructionByte + EofValidator.ONE_BYTE_LENGTH + j * EofValidator.TWO_BYTE_LENGTH, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); - var rjumpdest = offset + immediateValueSize + postInstructionByte; - if (rjumpdest < 0 || rjumpdest >= code.Length) + var offset = code.Slice(nextPosition + EofValidator.ONE_BYTE_LENGTH + j * EofValidator.TWO_BYTE_LENGTH, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + var rjumpDest = offset + immediateValueSize + nextPosition; + if (rjumpDest < 0 || rjumpDest >= code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Destination outside of Code bounds"); return false; } - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDest, ref rjumpdest); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDestinations, ref rjumpDest); } - BitmapHelper.HandleNumbits(immediateValueSize, dataBitmap, ref postInstructionByte); + + BitmapHelper.HandleNumbits(immediateValueSize, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.CALLF) { - if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Argument underflow"); return false; } - ushort targetSectionId = code.Slice(postInstructionByte, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + ushort targetSectionId = code.Slice(nextPosition, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); if (targetSectionId >= eofContainer.Header.CodeSections.Count) { @@ -782,9 +783,9 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } - ReadOnlySpan targetTypesection = eofContainer.TypeSections[targetSectionId].Span; + ReadOnlySpan targetTypeSection = eofContainer.TypeSections[targetSectionId].Span; - var targetSectionOutputCount = targetTypesection[OUTPUTS_OFFSET]; + var targetSectionOutputCount = targetTypeSection[OUTPUTS_OFFSET]; if (targetSectionOutputCount == 0x80) { @@ -793,7 +794,7 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, dataBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.RETF && isCurrentSectionNonReturning) { @@ -802,20 +803,20 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } else if (opcode is Instruction.DATALOADN) { - if (postInstructionByte + EofValidator.TWO_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.DATALOADN} Argument underflow"); return false; } - ushort dataSectionOffset = code.Slice(postInstructionByte, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + ushort dataSectionOffset = code.Slice(nextPosition, EofValidator.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}"); + 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.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, dataBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.RETURNCONTRACT) { @@ -837,16 +838,16 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } } - if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT} Argument underflow"); return false; } - ushort runtimeContainerId = code[postInstructionByte]; + ushort runtimeContainerId = code[nextPosition]; if (eofContainer.Header.ContainerSections is null || runtimeContainerId >= eofContainer.Header.ContainerSections?.Count) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate argument must be less than containersection.Count i.e: {eofContainer.Header.ContainerSections?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'s immediate argument must be less than containerSection.Count i.e: {eofContainer.Header.ContainerSections?.Count}"); return false; } @@ -859,49 +860,49 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode); - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, dataBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.EOFCREATE) { - if (postInstructionByte + EofValidator.ONE_BYTE_LENGTH > code.Length) + if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE} Argument underflow"); return false; } - var initcodeSectionId = code[postInstructionByte]; + int initCodeSectionId = code[nextPosition]; - if (eofContainer.Header.ContainerSections is null || initcodeSectionId >= eofContainer.Header.ContainerSections?.Count) + if (eofContainer.Header.ContainerSections is null || initCodeSectionId >= eofContainer.Header.ContainerSections?.Count) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {eofContainer.Header.CodeSections.Count}"); return false; } - if (containersWorklist.VisitedContainers[initcodeSectionId + 1] != 0 - && containersWorklist.VisitedContainers[initcodeSectionId + 1] != ValidationStrategy.ValidateInitcodeMode) + 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"); + 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); + containersWorklist.Enqueue(initCodeSectionId + 1, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateFullBody); - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, dataBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is >= Instruction.PUSH0 and <= Instruction.PUSH32) { - int len = opcode - Instruction.PUSH0; - if (postInstructionByte + len > code.Length) + 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.HandleNumbits(len, dataBitmap, ref postInstructionByte); + BitmapHelper.HandleNumbits(pushDataLength, invalidJumpDestinations, ref nextPosition); } - pos = postInstructionByte; + position = nextPosition; } - if (pos > code.Length) + if (position > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); return false; @@ -913,18 +914,24 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } - var result = !BitmapHelper.CheckCollision(dataBitmap, jumpDest); - if (!result) - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination"); + var result = BitmapHelper.CheckCollision(invalidJumpDestinations, jumpDestinations); + if (result) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination {result}"); + return false; + } if (!ValidateStackState(sectionId, code, eofContainer.TypeSection.Span)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Stack state"); return false; - return result; + } + return true; } finally { - ArrayPool.Shared.Return(dataBitmapArray); - ArrayPool.Shared.Return(jumpDestArray); + ArrayPool.Shared.Return(invalidJmpLocationArray); + ArrayPool.Shared.Return(jumpDestinationsArray); } } public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection) From 3d24a081d24448511f80c32e54b4646e410545cf Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 18 Sep 2024 13:36:16 +0100 Subject: [PATCH 149/255] Containers start at 1 --- .../EvmObjectFormat/Handlers/EofV1.cs | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 012a0d08c12..d7bed7d5e7f 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -266,7 +266,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => var numberOfContainerSections = GetUInt16(container, pos); sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * EofValidator.TWO_BYTE_LENGTH); - if (numberOfContainerSections is > MAXIMUM_NUM_CONTAINER_SECTIONS or 0) + if (numberOfContainerSections is > (MAXIMUM_NUM_CONTAINER_SECTIONS + 1) or 0) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, container sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); return false; @@ -504,10 +504,10 @@ private bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida var typeSection = header.TypeSection; (int typeSectionStart, int typeSectionSize) = (typeSection.Start, typeSection.Size); - if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS) + if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS + 1) { // move this check where `header.ExtraContainers.Count` is parsed - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, initcode Containers bount must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSections?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, initcode Containers count must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSections?.Count}"); return false; } @@ -670,6 +670,50 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } } } + else if (opcode is Instruction.RETURNCONTRACT) + { + if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); + return false; + } + else + { + if (containersWorklist.VisitedContainers[0] == ValidationStrategy.ValidateRuntimeMode) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); + return false; + } + else + { + containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateInitcodeMode; + } + } + + if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT} Argument underflow"); + return false; + } + + ushort runtimeContainerId = code[nextPosition]; + if (eofContainer.Header.ContainerSections is null || runtimeContainerId >= eofContainer.Header.ContainerSections?.Count) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'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.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); + return false; + } + + containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode); + + BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); + } else if (opcode is Instruction.RJUMP or Instruction.RJUMPI) { if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) @@ -818,50 +862,6 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } - else if (opcode is Instruction.RETURNCONTRACT) - { - if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); - return false; - } - else - { - if (containersWorklist.VisitedContainers[0] == ValidationStrategy.ValidateRuntimeMode) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); - return false; - } - else - { - containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateInitcodeMode; - } - } - - if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT} Argument underflow"); - return false; - } - - ushort runtimeContainerId = code[nextPosition]; - if (eofContainer.Header.ContainerSections is null || runtimeContainerId >= eofContainer.Header.ContainerSections?.Count) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'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.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); - return false; - } - - containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode); - - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); - } else if (opcode is Instruction.EOFCREATE) { if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) From 42199a2b7ee3e727f9ee068a34863ea5927eedcf Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 18 Sep 2024 13:51:48 +0100 Subject: [PATCH 150/255] Handle push0 properly --- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 2 ++ src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index a1df6e39dbd..0facb32c3b4 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -52,6 +52,8 @@ public static byte[] CreateCodeBitmap(ReadOnlySpan code, bool isEof = fals public static void HandleNumbits(int numbits, Span bitvec, scoped ref int pc) { + if (numbits == 0) return; + if (numbits >= 8) { for (; numbits >= 16; numbits -= 16) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index d7bed7d5e7f..fa7428e1c9d 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -773,7 +773,6 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); - } else if (opcode is Instruction.RJUMPV) { From 150855e77b4d1b3231e32a672b36686685d701d3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 23 Sep 2024 13:45:28 +0100 Subject: [PATCH 151/255] Update tests --- .../Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs index 7e83504cd00..a034d0e5c04 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.9" + ArchiveVersion = "eip7692@v1.1.0" }, $"fixtures/blockchain_tests/prague"); return loader.LoadTests().Cast(); } diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs index 56b21a193fd..08de594f5aa 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs @@ -20,7 +20,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.9" + ArchiveVersion = "eip7692@v1.1.0" }, $"fixtures/eof_tests/prague"); return loader.LoadTests().Cast().Select(t => new TestCaseData(t) .SetName(t.Name) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index 0f3a8acbfc7..cfbdca89b6a 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.0.9" + ArchiveVersion = "eip7692@v1.1.0" }, $"fixtures/state_tests/prague"); return loader.LoadTests().Cast(); } From 09de61c26fe3d8ae91778b3a0870206cf47b07c2 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 23 Sep 2024 13:53:23 +0100 Subject: [PATCH 152/255] Merge conflict --- .../TransactionProcessing/TransactionProcessor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index c9e33bff811..319473a4788 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -457,7 +457,7 @@ private TransactionResult BuildExecutionEnvironment( codeInfo.AnalyseInBackgroundIfRequired(); } - return new ExecutionEnvironment + env = new ExecutionEnvironment ( txExecutionContext: in executionContext, value: tx.Value, @@ -468,6 +468,8 @@ private TransactionResult BuildExecutionEnvironment( inputData: inputData, codeInfo: codeInfo ); + + return TransactionResult.Ok; } protected virtual bool ShouldValidate(ExecutionOptions opts) => !opts.HasFlag(ExecutionOptions.NoValidation); From 86645b95bc56cb1254be43ef117a0bf4ba1a18e7 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 24 Sep 2024 16:28:14 +0100 Subject: [PATCH 153/255] Output more info for failing tests --- src/Nethermind/Ethereum.Test.Base/EofTest.cs | 3 ++ .../Ethereum.Test.Base/EofTestBase.cs | 11 ++++- .../GeneralStateTestInfoJson.cs | 43 +++++++++++++++++-- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 16 ++++++- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/EofTest.cs b/src/Nethermind/Ethereum.Test.Base/EofTest.cs index 8176268bd7f..29d9af8b147 100644 --- a/src/Nethermind/Ethereum.Test.Base/EofTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/EofTest.cs @@ -25,4 +25,7 @@ public class EofTest : IEthereumTest public string? Category { get; set; } public string? LoadFailure { 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 index 9f7936c8c36..fe2333d7c27 100644 --- a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs @@ -29,7 +29,16 @@ protected static void Setup(ILogManager logManager) protected void RunCITest(EofTest test) { - Assert.That(RunTest(test, NullTxTracer.Instance), Is.EqualTo(test.Result.Success)); + 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) diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralStateTestInfoJson.cs b/src/Nethermind/Ethereum.Test.Base/GeneralStateTestInfoJson.cs index 17a520aa6d6..122d940fb5a 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralStateTestInfoJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralStateTestInfoJson.cs @@ -11,6 +11,10 @@ namespace Ethereum.Test.Base [JsonConverter(typeof(GeneralStateTestInfoConverter))] public class GeneralStateTestInfoJson { + public string? Description { get; set; } + public string? Url { get; set; } + public string? Spec { get; set; } + public Dictionary? Labels { get; set; } } @@ -22,6 +26,9 @@ public class GeneralStateTestInfoConverter : JsonConverter? labels = null; + string? description = null; + string? url = null; + string? spec = null; if (reader.TokenType == JsonTokenType.StartObject) { var depth = reader.CurrentDepth; @@ -31,10 +38,32 @@ public class GeneralStateTestInfoConverter : JsonConverter>(ref reader, options); + if (reader.ValueTextEquals("labels"u8)) + { + reader.Read(); + labels = JsonSerializer.Deserialize>(ref reader, options); + } + else if (reader.ValueTextEquals("description"u8)) + { + reader.Read(); + description = JsonSerializer.Deserialize(ref reader, options); + } + else if (reader.ValueTextEquals("url"u8)) + { + reader.Read(); + url = JsonSerializer.Deserialize(ref reader, options); + } + else if (reader.ValueTextEquals("reference-spec"u8)) + { + reader.Read(); + spec = JsonSerializer.Deserialize(ref reader, options); + } + else + { + reader.Skip(); + } } else { @@ -43,7 +72,13 @@ public class GeneralStateTestInfoConverter : JsonConverter ConvertToEofTests(string json) var name = namedTest.Key.Substring(index + 5); string category = namedTest.Key.Substring(0, index).Replace("tests/prague/eip7692_eof_v1/", ""); + string? description = null; + string? url = null; + string? spec = null; + var info = namedTest.Value?.Info; + if (info is not null) + { + description = info.Description; + url = info.Url; + spec = info.Spec; + } + foreach (KeyValuePair pair in namedTest.Value.Vectors) { VectorTestJson vectorJson = pair.Value; @@ -304,7 +315,10 @@ public static IEnumerable ConvertToEofTests(string json) EofTest test = new() { Name = $"{name}", - Category = $"{category} [{result.Key}]" + Category = $"{category} [{result.Key}]", + Url = url, + Description = description, + Spec = spec }; test.Vector = vector; From 8627577254af9d1759fc9673f9b5561498c3d8d3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 26 Sep 2024 12:48:31 +0100 Subject: [PATCH 154/255] Merge conflict --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d4c456b52bb..4107a57aa02 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2385,7 +2385,7 @@ private CallResult ExecuteCode Date: Thu, 26 Sep 2024 13:59:22 +0100 Subject: [PATCH 155/255] Update tests --- .../Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs | 2 +- .../Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs index a034d0e5c04..a266053833b 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.1.0" + ArchiveVersion = "eip7692@v1.1.1" }, $"fixtures/blockchain_tests/prague"); return loader.LoadTests().Cast(); } diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs index 08de594f5aa..54de71ff114 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs @@ -20,7 +20,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.1.0" + ArchiveVersion = "eip7692@v1.1.1" }, $"fixtures/eof_tests/prague"); return loader.LoadTests().Cast().Select(t => new TestCaseData(t) .SetName(t.Name) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs index cfbdca89b6a..c9061af1a92 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -21,7 +21,7 @@ private static IEnumerable LoadTests() TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.1.0" + ArchiveVersion = "eip7692@v1.1.1" }, $"fixtures/state_tests/prague"); return loader.LoadTests().Cast(); } From 370c2c358851ddc275ff913353801c9470e6fb70 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 4 Oct 2024 19:08:53 -0600 Subject: [PATCH 156/255] Fix remaining EOF validation issues (#7556) --- src/Nethermind/Ethereum.Test.Base/EofTest.cs | 4 +-- .../Ethereum.Test.Base/EofTestBase.cs | 3 +- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 6 ++++ .../Ethereum.Test.Base/VectorTestJson.cs | 1 + .../EvmObjectFormat/Handlers/EofV1.cs | 33 ++++++++++++++----- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/EofTest.cs b/src/Nethermind/Ethereum.Test.Base/EofTest.cs index 29d9af8b147..a773a6b9468 100644 --- a/src/Nethermind/Ethereum.Test.Base/EofTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/EofTest.cs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using Ethereum.Test.Base.Interfaces; -using System.Collections.Generic; -using System.Numerics; +using Nethermind.Evm.EvmObjectFormat; namespace Ethereum.Test.Base; public class Result @@ -16,6 +15,7 @@ public class Result public class VectorTest { public byte[] Code { get; set; } + public ValidationStrategy ContainerKind { get; set; } } public class EofTest : IEthereumTest diff --git a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs index fe2333d7c27..0a718285bcb 100644 --- a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs @@ -53,6 +53,7 @@ protected bool RunTest(EofTest test, ITxTracer txTracer) var vector = test.Vector; var code = vector.Code; + var strategy = vector.ContainerKind; var fork = test.Result.Fork switch { "Prague" => Nethermind.Specs.Forks.Prague.Instance, @@ -68,7 +69,7 @@ protected bool RunTest(EofTest test, ITxTracer txTracer) _ => throw new NotSupportedException($"Fork {test.Result.Fork} is not supported") }; - bool result = CodeDepositHandler.IsValidWithEofRules(fork, code, 1); + bool result = CodeDepositHandler.IsValidWithEofRules(fork, code, 1, strategy); return result; } } diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index f1d3e6dd24c..4f150159d35 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -12,6 +12,7 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Crypto; +using Nethermind.Evm.EvmObjectFormat; using Nethermind.Int256; using Nethermind.Serialization.Json; using Nethermind.Serialization.Rlp; @@ -309,6 +310,11 @@ public static IEnumerable ConvertToEofTests(string json) VectorTestJson vectorJson = pair.Value; VectorTest vector = new(); vector.Code = Bytes.FromHexString(vectorJson.Code); + vector.ContainerKind = + ("INITCODE".Equals(vectorJson.ContainerKind) + ? ValidationStrategy.ValidateInitcodeMode + : ValidationStrategy.ValidateRuntimeMode) + | ValidationStrategy.ValidateFullBody; foreach (var result in vectorJson.Results) { diff --git a/src/Nethermind/Ethereum.Test.Base/VectorTestJson.cs b/src/Nethermind/Ethereum.Test.Base/VectorTestJson.cs index db2d6398892..5466b7eb379 100644 --- a/src/Nethermind/Ethereum.Test.Base/VectorTestJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/VectorTestJson.cs @@ -8,5 +8,6 @@ namespace Ethereum.Test.Base; public class VectorTestJson { public string Code { get; set; } + public string ContainerKind { get; set; } public Dictionary Results { get; set; } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index fa7428e1c9d..e2efd6b8422 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -440,11 +440,7 @@ private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy val if (validationStrategy.HasFlag(ValidationStrategy.Validate)) { - // Clear the Initcode flag for subcontainer - ValidationStrategy subContainerValidation = validationStrategy & ~ValidationStrategy.ValidateInitcodeMode; - // Set the Runtime flag for subcontainer - subContainerValidation |= ValidationStrategy.ValidateRuntimeMode; - containers.Enqueue((new EofContainer(subsection, header.Value), subContainerValidation)); + containers.Enqueue((new EofContainer(subsection, header.Value), worklet.Strategy)); } } else @@ -637,6 +633,7 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali ReadOnlySpan currentTypeSection = eofContainer.TypeSections[sectionId].Span; var isCurrentSectionNonReturning = currentTypeSection[OUTPUTS_OFFSET] == 0x80; + bool hasRequiredSectionExit = isCurrentSectionNonReturning; int position; Instruction opcode = Instruction.STOP; @@ -710,7 +707,7 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } - containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode); + containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode | ValidationStrategy.ValidateFullBody); BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } @@ -736,6 +733,7 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali } else if (opcode is Instruction.JUMPF) { + hasRequiredSectionExit = true; if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} Argument underflow"); @@ -762,6 +760,12 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali return false; } + if (isCurrentSectionNonReturning && !isTargetSectionNonReturning) + { + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} from non-returning must target non-returning"); + return false; + } + sectionsWorklist.Enqueue(targetSectionId, strategy); BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } @@ -839,10 +843,15 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali sectionsWorklist.Enqueue(targetSectionId, strategy); BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } - else if (opcode is Instruction.RETF && isCurrentSectionNonReturning) + else if (opcode is Instruction.RETF) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, non returning sections are not allowed to use opcode {Instruction.RETF}"); - return false; + 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 is Instruction.DATALOADN) { @@ -913,6 +922,12 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali 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; + } + var result = BitmapHelper.CheckCollision(invalidJumpDestinations, jumpDestinations); if (result) { From 794027230c31de040c8aef43fe07717809da2ad0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 9 Oct 2024 10:00:58 +0100 Subject: [PATCH 157/255] Merge conflicts --- .../Nethermind.Evm.Benchmark/EvmBenchmarks.cs | 2 +- .../TransactionProcessor.cs | 202 +++++++++--------- 2 files changed, 102 insertions(+), 102 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index e854d7f2285..93a0c4548cf 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -55,7 +55,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(ByteCode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, []), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, [], codeInfoRepository), inputData: default ); diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 4c7a3d19c50..e1cd3eb6382 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -520,7 +520,7 @@ private TransactionResult BuildExecutionEnvironment( accessedAddresses.Add(recipient); accessedAddresses.Add(tx.SenderAddress!); - + TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, codeInfoRepository); ICodeInfo codeInfo = null; ReadOnlyMemory inputData = tx.IsMessageCall ? tx.Data ?? default : default; @@ -601,128 +601,128 @@ protected virtual void ExecuteEvmCall( } } - ExecutionType executionType = tx.IsContractCreation ? (tx.IsEofContractCreation ? ExecutionType.TXCREATE : ExecutionType.CREATE) : ExecutionType.TRANSACTION; + ExecutionType executionType = tx.IsContractCreation ? (tx.IsEofContractCreation ? ExecutionType.TXCREATE : ExecutionType.CREATE) : ExecutionType.TRANSACTION; - using (EvmState state = new(unspentGas, env, executionType, true, snapshot, false)) + using (EvmState state = new(unspentGas, env, executionType, true, snapshot, false)) + { + if (spec.UseTxAccessLists) { - if (spec.UseTxAccessLists) - { - state.WarmUp(tx.AccessList); // eip-2930 - } + state.WarmUp(tx.AccessList); // eip-2930 + } + + if (spec.UseHotAndColdStorage) + { + state.WarmUp(tx.SenderAddress); // eip-2929 + state.WarmUp(env.ExecutingAccount); // eip-2929 + } + + if (spec.AddCoinbaseToTxAccessList) + { + state.WarmUp(header.GasBeneficiary); + } + WarmUp(tx, header, spec, state, accessedAddresses); + substate = !tracer.IsTracingActions + ? VirtualMachine.Run(state, WorldState, tracer) + : VirtualMachine.Run(state, WorldState, tracer); + + unspentGas = state.GasAvailable; - if (spec.UseHotAndColdStorage) + if (tracer.IsTracingAccess) + { + tracer.ReportAccess(state.AccessedAddresses, state.AccessedStorageCells); + } + } + + if (substate.ShouldRevert || substate.IsError) + { + if (Logger.IsTrace) Logger.Trace("Restoring state from before transaction"); + WorldState.Restore(snapshot); + } + else + { + // tks: there is similar code fo contract creation from init and from CREATE + // this may lead to inconsistencies (however it is tested extensively in blockchain tests) + if (tx.IsLegacyContractCreation) + { + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Bytes.Length); + if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) { - state.WarmUp(tx.SenderAddress); // eip-2929 - state.WarmUp(env.ExecutingAccount); // eip-2929 + goto Fail; } - if (spec.AddCoinbaseToTxAccessList) + if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output.Bytes, 0)) { - state.WarmUp(header.GasBeneficiary); + goto Fail; } - WarmUp(tx, header, spec, state, accessedAddresses); - substate = !tracer.IsTracingActions - ? VirtualMachine.Run(state, WorldState, tracer) - : VirtualMachine.Run(state, WorldState, tracer); - unspentGas = state.GasAvailable; - - if (tracer.IsTracingAccess) + if (unspentGas >= codeDepositGasCost) { - tracer.ReportAccess(state.AccessedAddresses, state.AccessedStorageCells); + var code = substate.Output.Bytes.ToArray(); + _codeInfoRepository.InsertCode(WorldState, code, env.ExecutingAccount, spec); + + unspentGas -= codeDepositGasCost; } } - if (substate.ShouldRevert || substate.IsError) + if (tx.IsEofContractCreation) { - if (Logger.IsTrace) Logger.Trace("Restoring state from before transaction"); - WorldState.Restore(snapshot); - } - else - { - // tks: there is similar code fo contract creation from init and from CREATE - // this may lead to inconsistencies (however it is tested extensively in blockchain tests) - if (tx.IsLegacyContractCreation) + // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed + ReadOnlySpan auxExtraData = substate.Output.Bytes.Span; + EofCodeInfo deployCodeInfo = (EofCodeInfo)substate.Output.DeployCode; + + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, deployCodeInfo.MachineCode.Length + auxExtraData.Length); + if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, substate.Output.Bytes.Length); - if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) - { - goto Fail; - } - - if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output.Bytes, 0)) - { - goto Fail; - } - - if (unspentGas >= codeDepositGasCost) - { - var code = substate.Output.Bytes.ToArray(); - _codeInfoRepository.InsertCode(WorldState, code, env.ExecutingAccount, spec); - - unspentGas -= codeDepositGasCost; - } + goto Fail; } - if (tx.IsEofContractCreation) + byte[] bytecodeResultArray = null; + + // 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.MachineCode.Length + auxExtraData.Length]; + // 2 - 1 - 1 - copy old container + deployCodeInfo.MachineCode.Span.CopyTo(bytecodeResult); + // 2 - 1 - 2 - copy aux data to dataSection + auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.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) + + (deployCodeInfo.EofContainer.Header.ContainerSections is null + ? 0 // container section : (0 bytes if no container section is available) + : ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + + ONE_BYTE_LENGTH; // data section seperator + + ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); + bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); + bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); + + bytecodeResultArray = bytecodeResult.ToArray(); + + // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts + bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); + if (unspentGas >= codeDepositGasCost && !invalidCode) { - // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed - ReadOnlySpan auxExtraData = substate.Output.Bytes.Span; - EofCodeInfo deployCodeInfo = (EofCodeInfo)substate.Output.DeployCode; - - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, deployCodeInfo.MachineCode.Length + auxExtraData.Length); - if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) - { - goto Fail; - } - - byte[] bytecodeResultArray = null; - - // 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.MachineCode.Length + auxExtraData.Length]; - // 2 - 1 - 1 - copy old container - deployCodeInfo.MachineCode.Span.CopyTo(bytecodeResult); - // 2 - 1 - 2 - copy aux data to dataSection - auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.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) - + (deployCodeInfo.EofContainer.Header.ContainerSections is null - ? 0 // container section : (0 bytes if no container section is available) - : ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) - + ONE_BYTE_LENGTH; // data section seperator - - ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); - bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); - bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); - - bytecodeResultArray = bytecodeResult.ToArray(); - - // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - bool invalidCode = !(bytecodeResultArray.Length < spec.MaxCodeSize); - if (unspentGas >= codeDepositGasCost && !invalidCode) - { - // 4 - set state[new_address].code to the updated deploy container - // push new_address onto the stack (already done before the ifs) - _codeInfoRepository.InsertCode(WorldState, bytecodeResultArray, env.ExecutingAccount, spec); - unspentGas -= codeDepositGasCost; - } + // 4 - set state[new_address].code to the updated deploy container + // push new_address onto the stack (already done before the ifs) + _codeInfoRepository.InsertCode(WorldState, bytecodeResultArray, env.ExecutingAccount, spec); + unspentGas -= codeDepositGasCost; } + } - foreach (Address toBeDestroyed in substate.DestroyList) - { - if (Logger.IsTrace) - Logger.Trace($"Destroying account {toBeDestroyed}"); + foreach (Address toBeDestroyed in substate.DestroyList) + { + if (Logger.IsTrace) + Logger.Trace($"Destroying account {toBeDestroyed}"); - WorldState.ClearStorage(toBeDestroyed); - WorldState.DeleteAccount(toBeDestroyed); + WorldState.ClearStorage(toBeDestroyed); + WorldState.DeleteAccount(toBeDestroyed); - if (tracer.IsTracingRefunds) - tracer.ReportRefund(RefundOf.Destroy(spec.IsEip3529Enabled)); - } + if (tracer.IsTracingRefunds) + tracer.ReportRefund(RefundOf.Destroy(spec.IsEip3529Enabled)); + } statusCode = StatusCode.Success; } From d5b7fb19f66a222ee28e6c9af138dff6d0116222 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 15 Oct 2024 11:47:04 +0100 Subject: [PATCH 158/255] Merge conflict --- .../Nethermind.Evm/VirtualMachine.cs | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 420d066d85d..54ce9ab9e50 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -301,9 +301,9 @@ public TransactionSubstate Run(EvmState state, IWorldState worl : currentState.GasAvailable, callResult.Output.Bytes); } - else + else if (currentState.ExecutionType.IsAnyCreate()) { - if (currentState.ExecutionType.IsAnyCreate() && currentState.GasAvailable < codeDepositGasCost) + if (currentState.GasAvailable < codeDepositGasCost) { if (spec.ChargeForTopLevelCreate) { @@ -315,22 +315,19 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } } // Reject code starting with 0xEF if EIP-3541 is enabled. - else if (currentState.ExecutionType.IsAnyCreate() && CodeDepositHandler.CodeIsInvalid(spec, callResult.Output.Bytes, callResult.FromVersion)) + else if (CodeDepositHandler.CodeIsInvalid(spec, callResult.Output.Bytes, callResult.FromVersion)) { _txTracer.ReportActionError(EvmExceptionType.InvalidCode); } else { - if (currentState.ExecutionType.IsAnyCreate()) - { - _txTracer.ReportActionEnd(currentState.GasAvailable - codeDepositGasCost, currentState.To, callResult.Output.Bytes); - } - else - { - _txTracer.ReportActionEnd(currentState.GasAvailable, _returnDataBuffer); - } + _txTracer.ReportActionEnd(currentState.GasAvailable - codeDepositGasCost, currentState.To, callResult.Output.Bytes); } } + else + { + _txTracer.ReportActionEnd(currentState.GasAvailable, _returnDataBuffer); + } } return new TransactionSubstate( @@ -2111,21 +2108,46 @@ private CallResult ExecuteCode account = _state.GetCode(address); - if (spec.IsEofEnabled && IsEof(account, out _)) + if (env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(_state, address, spec, out Address delegatedAddress)) { - stack.PushBytes(EofHash256); + if (!_state.AccountExists(delegatedAddress)) + { + stack.PushZero(); + } + else + { + if (spec.IsEofEnabled) + { + ICodeInfo codeInfo = env.TxExecutionContext.CodeInfoRepository.GetCachedCodeInfo(_state, address, spec); + + if (IsEof(codeInfo.MachineCode, out _)) + { + stack.PushBytes(EofHash256); + break; + } + } + + stack.PushBytes(env.TxExecutionContext.CodeInfoRepository.GetExecutableCodeHash(_state, address, spec).BytesAsSpan); + } } else { + if (spec.IsEofEnabled) + { + Memory code = _state.GetCode(address); + if (IsEof(code, out _)) + { + stack.PushBytes(EofHash256); + break; + } + } + stack.PushBytes(_state.GetCodeHash(address).Bytes); } } From d9dd09e1daa6b3e8d0962b389262987469b82fa8 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 25 Oct 2024 23:59:55 -0600 Subject: [PATCH 159/255] Adaot to EEST Osaka Activation (#7657) --- .../{PragueEofTests.cs => OsakaEofTests.cs} | 8 +++--- ...PragueStateTests.cs => OsakaStateTests.cs} | 8 +++--- .../Ethereum.Blockchain.Test/EofTests.cs | 5 +++- .../Ethereum.Test.Base/EofTestBase.cs | 1 + .../Ethereum.Test.Base/JsonToEthereumTest.cs | 4 +-- .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 23 ++++++----------- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 ++ .../EvmObjectFormat/EofCodeInfo.cs | 2 ++ src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 1 + .../JavaScript/GethLikeJavaScriptTxTracer.cs | 2 +- .../Tracing/ParityStyle/ParityLikeTxTracer.cs | 2 +- .../Nethermind.Evm/VirtualMachine.cs | 25 ++++++++++++------- .../MainnetSpecProviderTests.cs | 9 +++++++ .../Nethermind.Specs/Forks/18_Prague.cs | 1 - .../Nethermind.Specs/Forks/19_Osaka.cs | 21 ++++++++++++++++ .../Nethermind.Specs/MainnetSpecProvider.cs | 5 ++-- .../Nethermind.Test.Runner/EofTestsRunner.cs | 2 +- .../StateTestTxTracer.cs | 2 +- 18 files changed, 80 insertions(+), 43 deletions(-) rename src/Nethermind/Ethereum.Blockchain.Pyspec.Test/{PragueEofTests.cs => OsakaEofTests.cs} (78%) rename src/Nethermind/Ethereum.Blockchain.Pyspec.Test/{PragueStateTests.cs => OsakaStateTests.cs} (77%) create mode 100644 src/Nethermind/Nethermind.Specs/Forks/19_Osaka.cs diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs similarity index 78% rename from src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs rename to src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs index 54de71ff114..4c56759ae0e 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs @@ -10,7 +10,7 @@ namespace Ethereum.Blockchain.Pyspec.Test; [TestFixture] [Parallelizable(ParallelScope.All)] -public class PragueEofTests : EofTestBase +public class OsakaEofTests : EofTestBase { [TestCaseSource(nameof(LoadTests))] public void Test(EofTest test) => RunCITest(test); @@ -19,9 +19,9 @@ private static IEnumerable LoadTests() { TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { - ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.1.1" - }, $"fixtures/eof_tests/prague"); + ArchiveName = "fixtures_eip7692-osaka.tar.gz", + ArchiveVersion = "eip7692@v2.0.0" + }, $"fixtures/eof_tests/osaka"); return loader.LoadTests().Cast().Select(t => new TestCaseData(t) .SetName(t.Name) .SetCategory(t.Category)); diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs similarity index 77% rename from src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs rename to src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs index 38e604c3c2b..6e9f00834d0 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs @@ -12,7 +12,7 @@ namespace Ethereum.Blockchain.Pyspec.Test; [TestFixture] [Parallelizable(ParallelScope.All)] [Explicit("These tests are not ready yet")] -public class PragueStateTests : GeneralStateTestBase +public class OsakaStateTests : GeneralStateTestBase { [TestCaseSource(nameof(LoadTests))] public void Test(GeneralStateTest test) => RunTest(test).Pass.Should().BeTrue(); @@ -21,9 +21,9 @@ private static IEnumerable LoadTests() { TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { - ArchiveName = "fixtures_eip7692.tar.gz", - ArchiveVersion = "eip7692@v1.1.1" - }, $"fixtures/state_tests/prague"); + ArchiveName = "fixtures_eip7692-osaka.tar.gz", + ArchiveVersion = "eip7692@v2.0.0" + }, $"fixtures/state_tests/osaka"); return loader.LoadTests().Cast(); } } diff --git a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs index e4976b2d95c..c23f2e75f58 100644 --- a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using System.Linq; using Ethereum.Test.Base; @@ -15,7 +16,9 @@ public class EOFTests : GeneralStateTestBase [TestCaseSource(nameof(LoadTests))] public void Test(GeneralStateTest test) { - Assert.That(RunTest(test).Pass, Is.True); + // Legacy ethereum/tests EOF tests currently are based on a Prague spec, which lacks EOF. + // All EOF tests are now a part of EEST + //Assert.That(RunTest(test).Pass, Is.True); } public static IEnumerable LoadTests() diff --git a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs index fadc94a299e..3f0d38ee79a 100644 --- a/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/EofTestBase.cs @@ -56,6 +56,7 @@ protected bool RunTest(EofTest test, ITxTracer txTracer) var strategy = vector.ContainerKind; var 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, diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 7017d0b412c..dc436ce6a5c 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -61,6 +61,7 @@ private static IReleaseSpec ParseSpec(string network) "Cancun" => Cancun.Instance, "Paris" => Paris.Instance, "Prague" => Prague.Instance, + "Osaka" => Osaka.Instance, _ => throw new NotSupportedException() }; } @@ -310,7 +311,7 @@ public static IEnumerable ConvertToEofTests(string json) { var index = namedTest.Key.IndexOf(".py::"); var name = namedTest.Key.Substring(index + 5); - string category = namedTest.Key.Substring(0, index).Replace("tests/prague/eip7692_eof_v1/", ""); + string category = namedTest.Key.Substring(0, index).Replace("tests/osaka/eip7692_eof_v1/", ""); string? description = null; string? url = null; @@ -365,7 +366,6 @@ public static IEnumerable ConvertStateTest(string json) List tests = new(); foreach (KeyValuePair namedTest in testsInFile) { - Console.WriteLine($"Loading {namedTest.Key}\n {namedTest.Value.Post}"); tests.AddRange(Convert(namedTest.Key, namedTest.Value)); } diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index f783f4d18d5..abf2c78e3cd 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -10,6 +10,7 @@ using Nethermind.Core.Test; using Nethermind.Logging; using Nethermind.Specs; +using Nethermind.Specs.Forks; using NUnit.Framework; namespace Nethermind.Evm.Test @@ -75,19 +76,8 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase ConstantinopleFixInstructions.Union( new[] { Instruction.SELFBALANCE, Instruction.CHAINID }).ToArray(); - private static readonly Instruction[] BerlinInstructions = - IstanbulInstructions.Union( - // new[] - // { - // Instruction.BEGINSUB, - // Instruction.JUMPSUB, - // Instruction.RETURNSUB - // } - new Instruction[] { } - ).ToArray(); - private static readonly Instruction[] LondonInstructions = - BerlinInstructions.Union( + IstanbulInstructions.Union( new Instruction[] { Instruction.BASEFEE @@ -114,7 +104,7 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase } ).ToArray(); - private static readonly Instruction[] PragueInstructions = + private static readonly Instruction[] OsakaInstructions = CancunInstructions.Union( new Instruction[] { @@ -150,12 +140,13 @@ private readonly Dictionary _validOpcodes {(ForkActivation)MainnetSpecProvider.ConstantinopleFixBlockNumber, ConstantinopleFixInstructions}, {(ForkActivation)MainnetSpecProvider.IstanbulBlockNumber, IstanbulInstructions}, {(ForkActivation)MainnetSpecProvider.MuirGlacierBlockNumber, IstanbulInstructions}, - {(ForkActivation)MainnetSpecProvider.BerlinBlockNumber, BerlinInstructions}, + {(ForkActivation)MainnetSpecProvider.BerlinBlockNumber, IstanbulInstructions}, {(ForkActivation)MainnetSpecProvider.LondonBlockNumber, LondonInstructions}, {MainnetSpecProvider.ShanghaiActivation, ShanghaiInstructions}, {MainnetSpecProvider.CancunActivation, CancunInstructions}, - {MainnetSpecProvider.PragueActivation, PragueInstructions}, - {(long.MaxValue, ulong.MaxValue), PragueInstructions} + {MainnetSpecProvider.PragueActivation, CancunInstructions}, + {MainnetSpecProvider.OsakaActivation, OsakaInstructions}, + {(long.MaxValue, ulong.MaxValue), OsakaInstructions} }; private const string InvalidOpCodeErrorMessage = "BadInstruction"; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 5cf63dcc7aa..9efaa83028d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -63,5 +63,7 @@ public SectionHeader CodeSectionOffset(int idx) public SectionHeader? ContainerSectionOffset(int idx) => throw new UnreachableException(); + + public int PcOffset() => 0; } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index 296a1bc101d..cf3e51e0802 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -23,6 +23,8 @@ public class EofCodeInfo : ICodeInfo 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) { ReadOnlySpan typesectionSpan = EofContainer.TypeSections[index].Span; diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index 4db7c0ee0dc..bf7f9b24224 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -20,6 +20,7 @@ public interface ICodeInfo ReadOnlyMemory ContainerSection => Memory.Empty; SectionHeader CodeSectionOffset(int idx); SectionHeader? ContainerSectionOffset(int idx); + int PcOffset(); (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); void AnalyseInBackgroundIfRequired() { } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs index dde143ac7e3..9d90e6c2654 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs @@ -101,7 +101,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) { - _log.pc = pc; + _log.pc = pc + env.CodeInfo.PcOffset(); _log.op = new Log.Opcode(opcode); _log.gas = gas; _log.depth = env.GetGethTraceDepth(); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs index a7b9a36f0d2..3ca76729d2a 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs @@ -261,7 +261,7 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe { ParityVmOperationTrace operationTrace = new(); _gasAlreadySetForCurrentOp = false; - operationTrace.Pc = pc; + operationTrace.Pc = pc + env.CodeInfo.PcOffset(); operationTrace.Cost = gas; _currentOperation = operationTrace; _currentPushList.Clear(); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 54ce9ab9e50..82ec271e5bd 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -496,7 +496,9 @@ public TransactionSubstate Run(EvmState state, IWorldState worl { worldState.Restore(previousState.Snapshot); _returnDataBuffer = callResult.Output.Bytes; - previousCallResult = previousState.ExecutionType.IsAnyCallEof() ? EofStatusCode.RevertBytes : StatusCode.FailureBytes; + previousCallResult = previousState.ExecutionType.IsAnyCallEof() + ? (callResult.PrecompileSuccess is not null ? EofStatusCode.FailureBytes : EofStatusCode.RevertBytes) + : StatusCode.FailureBytes; previousCallOutput = callResult.Output.Bytes.Span.SliceWithZeroPadding(0, Math.Min(callResult.Output.Bytes.Length, (int)previousState.OutputLength)); previousCallOutputDestination = (ulong)previousState.OutputDestination; @@ -2751,25 +2753,29 @@ private EvmExceptionType InstructionEofCall= MaxCallDepth) { returnData = CallResult.BoxedEmpty; @@ -2832,7 +2838,8 @@ private EvmExceptionType InstructionEofCall callData = vmState.Memory.Load(in dataOffset, dataLength); Snapshot snapshot = _state.TakeSnapshot(); - _state.SubtractFromBalance(caller, callValue, spec); + _state.SubtractFromBalance(caller, transferValue, spec); ExecutionEnvironment callEnv = new ( @@ -2867,7 +2874,7 @@ private EvmExceptionType InstructionEofCall LazyInitializer.EnsureInitialized(ref _instance, () => new Osaka()); +} diff --git a/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs b/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs index 4aa6c8d8c63..d9c519e05d7 100644 --- a/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs @@ -49,7 +49,8 @@ public IReleaseSpec GetSpec(ForkActivation forkActivation) => { Timestamp: null } or { Timestamp: < ShanghaiBlockTimestamp } => Paris.Instance, { Timestamp: < CancunBlockTimestamp } => Shanghai.Instance, { Timestamp: < PragueBlockTimestamp } => Cancun.Instance, - _ => Prague.Instance + { Timestamp: < OsakaBlockTimestamp } => Prague.Instance, + _ => Osaka.Instance }; public void UpdateMergeTransitionInfo(long? blockNumber, UInt256? terminalTotalDifficulty = null) @@ -90,7 +91,7 @@ public void UpdateMergeTransitionInfo(long? blockNumber, UInt256? terminalTotalD ShanghaiActivation, CancunActivation, PragueActivation, - //OsakaActivation + OsakaActivation, }; public static MainnetSpecProvider Instance { get; } = new(); diff --git a/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs b/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs index ea684283a49..b17fe4ba9d0 100644 --- a/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs +++ b/src/Nethermind/Nethermind.Test.Runner/EofTestsRunner.cs @@ -32,7 +32,7 @@ public IEnumerable RunTests() } else { - var result = new EthereumTestResult(test.Name, "Prague", RunTest(test)); + var result = new EthereumTestResult(test.Name, "Osaka", RunTest(test)); testResults.Add(result); if (result.Pass) WriteGreen("PASS"); diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs index 2d00f475f49..505d822302b 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs @@ -54,7 +54,7 @@ public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnv bool isPostMerge = env.IsPostMerge(); _gasAlreadySetForCurrentOp = false; _traceEntry = new StateTestTxTraceEntry(); - _traceEntry.Pc = pc; + _traceEntry.Pc = pc + env.CodeInfo.PcOffset(); _traceEntry.Operation = (byte)opcode; _traceEntry.OperationName = opcode.GetName(isPostMerge); _traceEntry.Gas = gas; From 17dd735475f3d913ab2ccf69e0b5c6b0e96192f3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 11:04:58 +0000 Subject: [PATCH 160/255] Formatting & missing ref --- src/Nethermind/Directory.Packages.props | 1 + src/Nethermind/Nethermind.Evm/EvmState.cs | 2 +- .../Tracing/ParityStyle/ParityLikeTxTracer.cs | 26 ++++++++--------- .../TransactionProcessor.cs | 2 +- .../Nethermind.Test.Runner/Program.cs | 28 +++++++++---------- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/Nethermind/Directory.Packages.props b/src/Nethermind/Directory.Packages.props index 1bf4823b2fb..82b307f3e37 100644 --- a/src/Nethermind/Directory.Packages.props +++ b/src/Nethermind/Directory.Packages.props @@ -13,6 +13,7 @@ + diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index 0ab8d28f253..014bd05f308 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -32,7 +32,7 @@ public sealed class EvmState : IDisposable // TODO: rename to CallState public int ReturnStackHead; internal ExecutionType ExecutionType { get; private set; } // TODO: move to CallEnv public int ProgramCounter { get; set; } - public int FunctionIndex { 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 diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs index 70145401617..d5f690c1522 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs @@ -71,12 +71,12 @@ private static string GetCallType(ExecutionType executionType) { return executionType switch { - ExecutionType.CREATE or ExecutionType.CREATE2 or ExecutionType.EOFCREATE or ExecutionType.TXCREATE => "create", - ExecutionType.CALL or ExecutionType.TRANSACTION => "call", - ExecutionType.DELEGATECALL => "delegatecall", - ExecutionType.STATICCALL => "staticcall", - ExecutionType.CALLCODE => "callcode", - _ => throw new NotSupportedException($"Parity trace call type is undefined for {executionType}") + ExecutionType.CREATE or ExecutionType.CREATE2 or ExecutionType.EOFCREATE or ExecutionType.TXCREATE => "create", + ExecutionType.CALL or ExecutionType.TRANSACTION => "call", + ExecutionType.DELEGATECALL => "delegatecall", + ExecutionType.STATICCALL => "staticcall", + ExecutionType.CALLCODE => "callcode", + _ => throw new NotSupportedException($"Parity trace call type is undefined for {executionType}") }; } @@ -84,8 +84,8 @@ private static string GetActionType(ExecutionType executionType) { return executionType switch { - ExecutionType.CREATE or ExecutionType.CREATE2 or ExecutionType.EOFCREATE or ExecutionType.TXCREATE => "create", - _ => "call" + ExecutionType.CREATE or ExecutionType.CREATE2 or ExecutionType.EOFCREATE or ExecutionType.TXCREATE => "create", + _ => "call" }; } @@ -379,11 +379,11 @@ public override void ReportAction(long gas, UInt256 value, Address from, Address { return callType switch { - ExecutionType.CREATE => "create", - ExecutionType.CREATE2 => "create2", - ExecutionType.EOFCREATE => "create3", - ExecutionType.TXCREATE => "create4", - _ => null + ExecutionType.CREATE => "create", + ExecutionType.CREATE2 => "create2", + ExecutionType.EOFCREATE => "create3", + ExecutionType.TXCREATE => "create4", + _ => null }; } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 075d224f506..d109fd148f5 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -570,7 +570,7 @@ private TransactionResult BuildExecutionEnvironment( { if (spec.UseTxAccessLists) accessTracker.WarmUp(tx.AccessList); // eip-2930 - + if (spec.AddCoinbaseToTxAccessList) accessTracker.WarmUp(blCtx.Header.GasBeneficiary!); diff --git a/src/Nethermind/Nethermind.Test.Runner/Program.cs b/src/Nethermind/Nethermind.Test.Runner/Program.cs index 8ddca2554b6..339b3f57b76 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Test.Runner/Program.cs @@ -113,20 +113,20 @@ private static async Task RunBlockTest(string path, Func testRunnerBuilder) - { - ITestSourceLoader source = Path.HasExtension(path) - ? new TestsSourceLoader(new LoadEofTestFileStrategy(), path) - : new TestsSourceLoader(new LoadEofTestsStrategy(), path); - await testRunnerBuilder(source).RunTestsAsync(); - } + private static async Task RunEofTest(string path, Func testRunnerBuilder) + { + ITestSourceLoader source = Path.HasExtension(path) + ? new TestsSourceLoader(new LoadEofTestFileStrategy(), path) + : new TestsSourceLoader(new LoadEofTestsStrategy(), path); + await testRunnerBuilder(source).RunTestsAsync(); + } - private static void RunStateTest(string path, Func testRunnerBuilder) - { - ITestSourceLoader source = Path.HasExtension(path) - ? new TestsSourceLoader(new LoadGeneralStateTestFileStrategy(), path) - : new TestsSourceLoader(new LoadGeneralStateTestsStrategy(), path); - testRunnerBuilder(source).RunTests(); - } + private static void RunStateTest(string path, Func testRunnerBuilder) + { + ITestSourceLoader source = Path.HasExtension(path) + ? new TestsSourceLoader(new LoadGeneralStateTestFileStrategy(), path) + : new TestsSourceLoader(new LoadGeneralStateTestsStrategy(), path); + testRunnerBuilder(source).RunTests(); } } +} From c33fa89a216c65089f718bcd2e16a9bc8acd9135 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 11:11:29 +0000 Subject: [PATCH 161/255] Fix test --- src/Nethermind/Nethermind.Test.Runner/Program.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Test.Runner/Program.cs b/src/Nethermind/Nethermind.Test.Runner/Program.cs index 339b3f57b76..7225f1fdeee 100644 --- a/src/Nethermind/Nethermind.Test.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Test.Runner/Program.cs @@ -86,7 +86,7 @@ private static async Task Run(ParseResult parseResult, CancellationToken ca if (parseResult.GetValue(Options.BlockTest)) await RunBlockTest(input, source => new BlockchainTestsRunner(source, parseResult.GetValue(Options.Filter))); else if (parseResult.GetValue(Options.EofTest)) - await RunEofTest(input, source => new EofTestsRunner(source, parseResult.GetValue(Options.Filter))); + RunEofTest(input, source => new EofTestsRunner(source, parseResult.GetValue(Options.Filter))); else RunStateTest(input, source => new StateTestsRunner(source, whenTrace, !parseResult.GetValue(Options.ExcludeMemory), @@ -113,12 +113,12 @@ private static async Task RunBlockTest(string path, Func testRunnerBuilder) + private static void RunEofTest(string path, Func testRunnerBuilder) { ITestSourceLoader source = Path.HasExtension(path) ? new TestsSourceLoader(new LoadEofTestFileStrategy(), path) : new TestsSourceLoader(new LoadEofTestsStrategy(), path); - await testRunnerBuilder(source).RunTestsAsync(); + testRunnerBuilder(source).RunTests(); } private static void RunStateTest(string path, Func testRunnerBuilder) @@ -129,4 +129,3 @@ private static void RunStateTest(string path, Func Date: Tue, 28 Jan 2025 11:22:00 +0000 Subject: [PATCH 162/255] csproj --- .../Ethereum.Blockchain.Pyspec.Test.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/Ethereum.Blockchain.Pyspec.Test.csproj b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/Ethereum.Blockchain.Pyspec.Test.csproj index e450720acda..865da09704e 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/Ethereum.Blockchain.Pyspec.Test.csproj +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/Ethereum.Blockchain.Pyspec.Test.csproj @@ -4,11 +4,11 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive all - all From 2b19f4ced749c9ff1f97ea2765ef0d3aebaa77f8 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 13:55:19 +0000 Subject: [PATCH 163/255] Fix create gas --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 6cefc925d0d..f845dfd3915 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2092,9 +2092,7 @@ private CallResult ExecuteCode Date: Tue, 28 Jan 2025 14:08:41 +0000 Subject: [PATCH 164/255] fix build --- .../PragueBlockchainTests.cs | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs deleted file mode 100644 index 2474c0cfa2b..00000000000 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockchainTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Ethereum.Test.Base; -using NUnit.Framework; - -namespace Ethereum.Blockchain.Pyspec.Test; - -[TestFixture] -[Parallelizable(ParallelScope.All)] -[Explicit("These tests are not ready yet")] -public class PragueBlockChainTests : BlockchainTestBase -{ - [TestCaseSource(nameof(LoadTests))] - public async Task Test(BlockchainTest test) => await RunTest(test); - - private static IEnumerable LoadTests() - { - TestsSourceLoader loader = new(new LoadPyspecTestsStrategy(), $"fixtures/blockchain_tests/prague"); - return loader.LoadTests().OfType(); - } -} From 50955b8fce9fb08f0cb2eb8a1189972b8dd17639 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 15:06:44 +0000 Subject: [PATCH 165/255] Fix Evm tests --- .../Nethermind.Evm/VirtualMachine.cs | 38 +++---------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f845dfd3915..a2df1e7eae3 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2091,49 +2091,23 @@ private CallResult ExecuteCode code = _state.GetCode(address); + if (IsEof(code, out _)) { - stack.PushZero(); - } - else - { - if (spec.IsEofEnabled) - { - ICodeInfo codeInfo = env.TxExecutionContext.CodeInfoRepository.GetCachedCodeInfo(_state, address, spec); - - if (IsEof(codeInfo.MachineCode, out _)) - { - stack.PushBytes(EofHash256); - break; - } - } - - stack.PushBytes(env.TxExecutionContext.CodeInfoRepository.GetExecutableCodeHash(_state, address, spec).BytesAsSpan); + stack.PushBytes(EofHash256); + break; } } - else - { - if (spec.IsEofEnabled) - { - Memory code = _state.GetCode(address); - if (IsEof(code, out _)) - { - stack.PushBytes(EofHash256); - break; - } - } - stack.PushBytes(_state.GetCodeHash(address).Bytes); - } + stack.PushBytes(_state.GetCodeHash(address).Bytes); } break; From 7415eea3d80c2e68deae2077477e372b5a55f5a0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 16:27:13 +0000 Subject: [PATCH 166/255] Use correct stack --- src/Nethermind/Nethermind.Evm/EvmStack.cs | 25 -------------- src/Nethermind/Nethermind.Evm/EvmState.cs | 38 --------------------- src/Nethermind/Nethermind.Evm/StackPool.cs | 39 +++++++++++++++++++--- 3 files changed, 34 insertions(+), 68 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 2d1e743b803..6f276238e3a 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -426,28 +426,3 @@ private readonly void Trace(int depth) } } } - -public static class 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; - - [StackTraceHidden] - [DoesNotReturn] - internal static void ThrowEvmStackUnderflowException() - { - Metrics.EvmExceptions++; - throw new EvmStackUnderflowException(); - } - - [StackTraceHidden] - [DoesNotReturn] - internal static void ThrowEvmStackOverflowException() - { - Metrics.EvmExceptions++; - throw new EvmStackOverflowException(); - } -} diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index 014bd05f308..2ca2694c300 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -235,42 +235,4 @@ public struct ReturnState public int Offset; public int Height; } - - private class StackPool - { - private readonly int _maxCallStackDepth; - private readonly Stack<(byte[] dataStack, ReturnState[] returnStack)> _stackPool; - private int _stackPoolDepth; - - public StackPool(int maxCallStackDepth = VirtualMachine.MaxCallDepth * 2) - { - _maxCallStackDepth = maxCallStackDepth; - _stackPool = new Stack<(byte[], ReturnState[])>(32); - } - - public void ReturnStacks(byte[] dataStack, ReturnState[] returnStack) - { - _stackPool.Push((dataStack, returnStack)); - } - - public (byte[], ReturnState[]) RentStacks() - { - if (_stackPool.TryPop(out var result)) - { - return result; - } - - _stackPoolDepth++; - if (_stackPoolDepth > _maxCallStackDepth) - { - // EvmStack.ThrowEvmStackOverflowException(); - throw new InvalidOperationException("EVM stack overflow"); - } - - return ( - new byte[(EvmStack.MaxStackSize + EvmStack.RegisterLength) * 32], - new ReturnState[EvmStack.ReturnStackSize] - ); - } - } } diff --git a/src/Nethermind/Nethermind.Evm/StackPool.cs b/src/Nethermind/Nethermind.Evm/StackPool.cs index 5564fbe045c..2a2c0068f26 100644 --- a/src/Nethermind/Nethermind.Evm/StackPool.cs +++ b/src/Nethermind/Nethermind.Evm/StackPool.cs @@ -2,6 +2,10 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; + +using static Nethermind.Evm.EvmState; namespace Nethermind.Evm; @@ -9,10 +13,10 @@ internal class StackPool { // Also have parallel prewarming and Rpc calls private const int MaxStacksPooled = VirtualMachine.MaxCallDepth * 2; - private readonly struct StackItem(byte[] dataStack, int[] returnStack) + private readonly struct StackItem(byte[] dataStack, ReturnState[] returnStack) { public readonly byte[] DataStack = dataStack; - public readonly int[] ReturnStack = returnStack; + public readonly ReturnState[] ReturnStack = returnStack; } private readonly ConcurrentQueue _stackPool = new(); @@ -23,7 +27,7 @@ private readonly struct StackItem(byte[] dataStack, int[] returnStack) /// /// /// - public void ReturnStacks(byte[] dataStack, int[] returnStack) + public void ReturnStacks(byte[] dataStack, ReturnState[] returnStack) { if (_stackPool.Count <= MaxStacksPooled) { @@ -31,7 +35,7 @@ public void ReturnStacks(byte[] dataStack, int[] returnStack) } } - public (byte[], int[]) RentStacks() + public (byte[], ReturnState[]) RentStacks() { if (_stackPool.TryDequeue(out StackItem result)) { @@ -41,8 +45,33 @@ public void ReturnStacks(byte[] dataStack, int[] returnStack) return ( new byte[(EvmStack.MaxStackSize + EvmStack.RegisterLength) * 32], - new int[EvmStack.ReturnStackSize] + new ReturnState[EvmStack.ReturnStackSize] ); } } +internal static class 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; + + [StackTraceHidden] + [DoesNotReturn] + internal static void ThrowEvmStackUnderflowException() + { + Metrics.EvmExceptions++; + throw new EvmStackUnderflowException(); + } + + [StackTraceHidden] + [DoesNotReturn] + internal static void ThrowEvmStackOverflowException() + { + Metrics.EvmExceptions++; + throw new EvmStackOverflowException(); + } +} + From 4a22a1c93a5ee54b888f2819916e8c2a16c7c951 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 16:32:05 +0000 Subject: [PATCH 167/255] Compile error --- src/Nethermind/Nethermind.Evm/StackPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/StackPool.cs b/src/Nethermind/Nethermind.Evm/StackPool.cs index 2a2c0068f26..07cc466777f 100644 --- a/src/Nethermind/Nethermind.Evm/StackPool.cs +++ b/src/Nethermind/Nethermind.Evm/StackPool.cs @@ -50,7 +50,7 @@ public void ReturnStacks(byte[] dataStack, ReturnState[] returnStack) } } -internal static class EvmStack +public static class EvmStack { public const int RegisterLength = 1; public const int MaxStackSize = 1025; From 0e61f6ede515947060c18753e15892fe8f794a74 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 17:19:52 +0000 Subject: [PATCH 168/255] Update Eof tests --- .../OsakaEofTests.cs | 4 ++-- .../OsakaStateTests.cs | 4 ++-- .../Ethereum.Blockchain.Test/EofTests.cs | 15 +++++++++------ .../TransactionProcessing/TransactionProcessor.cs | 3 ++- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs index 4c56759ae0e..ecba81bbd42 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaEofTests.cs @@ -19,8 +19,8 @@ private static IEnumerable LoadTests() { TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { - ArchiveName = "fixtures_eip7692-osaka.tar.gz", - ArchiveVersion = "eip7692@v2.0.0" + ArchiveName = "fixtures_eip7692.tar.gz", + ArchiveVersion = "eip7692@v2.1.0" }, $"fixtures/eof_tests/osaka"); return loader.LoadTests().Cast().Select(t => new TestCaseData(t) .SetName(t.Name) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs index 6e9f00834d0..2f9de13ce5d 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs @@ -21,8 +21,8 @@ private static IEnumerable LoadTests() { TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { - ArchiveName = "fixtures_eip7692-osaka.tar.gz", - ArchiveVersion = "eip7692@v2.0.0" + ArchiveName = "fixtures_eip7692.tar.gz", + ArchiveVersion = "eip7692@v2.1.0" }, $"fixtures/state_tests/osaka"); return loader.LoadTests().Cast(); } diff --git a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs index c23f2e75f58..6e28b99d682 100644 --- a/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Test/EofTests.cs @@ -11,6 +11,7 @@ namespace Ethereum.Blockchain.Test; [TestFixture] [Parallelizable(ParallelScope.All)] +[Explicit("Legacy ethereum/tests EOF tests currently are based on a Prague spec, which lacks EOF.")] public class EOFTests : GeneralStateTestBase { [TestCaseSource(nameof(LoadTests))] @@ -23,11 +24,13 @@ public void Test(GeneralStateTest test) public static IEnumerable LoadTests() { - var eip3540Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP3540").LoadTests()); - var eip3670Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP3670").LoadTests()); - var eip4200Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP4200").LoadTests()); - var eip4750Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP4750").LoadTests()); - var eip5450Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP5450").LoadTests()); - return eip3540Loader.Concat(eip3670Loader).Concat(eip4200Loader).Concat(eip4750Loader).Concat(eip5450Loader); + return []; + + //var eip3540Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP3540").LoadTests()); + //var eip3670Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP3670").LoadTests()); + //var eip4200Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP4200").LoadTests()); + //var eip4750Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP4750").LoadTests()); + //var eip5450Loader = (IEnumerable)(new TestsSourceLoader(new LoadEofTestsStrategy(), "stEIP5450").LoadTests()); + //return eip3540Loader.Concat(eip3670Loader).Concat(eip4200Loader).Concat(eip4750Loader).Concat(eip5450Loader); } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index d109fd148f5..0674fdc6132 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -548,7 +548,7 @@ private TransactionResult BuildExecutionEnvironment( TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, codeInfoRepository); Address? delegationAddress = null; - ICodeInfo codeInfo = null; + ICodeInfo? codeInfo = null; ReadOnlyMemory inputData = tx.IsMessageCall ? tx.Data ?? default : default; if (tx.IsContractCreation) { @@ -565,6 +565,7 @@ private TransactionResult BuildExecutionEnvironment( if (delegationAddress is not null) accessTracker.WarmUp(delegationAddress); } + codeInfo ??= CodeInfo.Empty; codeInfo.AnalyseInBackgroundIfRequired(); if (spec.UseHotAndColdStorage) { From 50e12205bc6ce61072c46cbe248692bab8c7a416 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 17:37:10 +0000 Subject: [PATCH 169/255] Update Eof tests --- .../OsakaBlockChainTests.cs | 30 +++++++++++++++++++ .../OsakaStateTests.cs | 7 +++-- 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs new file mode 100644 index 00000000000..c4b8e1a7512 --- /dev/null +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ethereum.Test.Base; +using NUnit.Framework; + +namespace Ethereum.Blockchain.Pyspec.Test; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class OsakaBlockChainTests : BlockchainTestBase +{ + [TestCaseSource(nameof(LoadTests))] + public async Task Test(BlockchainTest test) => await RunTest(test); + + private static IEnumerable LoadTests() + { + TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() + { + ArchiveName = "fixtures_eip7692.tar.gz", + ArchiveVersion = "eip7692@v2.1.0" + }, $"fixtures/blockchain_tests/osaka"); + return loader.LoadTests().OfType().Select(t => new TestCaseData(t) + .SetName(t.Name) + .SetCategory(t.Category)); + } +} diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs index 2f9de13ce5d..315a9a3454d 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs @@ -11,19 +11,20 @@ namespace Ethereum.Blockchain.Pyspec.Test; [TestFixture] [Parallelizable(ParallelScope.All)] -[Explicit("These tests are not ready yet")] public class OsakaStateTests : GeneralStateTestBase { [TestCaseSource(nameof(LoadTests))] public void Test(GeneralStateTest test) => RunTest(test).Pass.Should().BeTrue(); - private static IEnumerable LoadTests() + private static IEnumerable LoadTests() { TestsSourceLoader loader = new(new LoadPyspecTestsStrategy() { ArchiveName = "fixtures_eip7692.tar.gz", ArchiveVersion = "eip7692@v2.1.0" }, $"fixtures/state_tests/osaka"); - return loader.LoadTests().Cast(); + return loader.LoadTests().Cast().Select(t => new TestCaseData(t) + .SetName(t.Name) + .SetCategory(t.Category)); } } From 4dca7a476e07296b15d071a8a65b1922ea289706 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 18:43:11 +0000 Subject: [PATCH 170/255] Categorize tests better --- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 57123e20c3c..acd23c57ce5 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -229,11 +229,11 @@ public static Transaction Convert(LegacyTransactionJson transactionJson) return transaction; } - public static IEnumerable Convert(string name, GeneralStateTestJson testJson) + public static IEnumerable Convert(string name, string category, GeneralStateTestJson testJson) { if (testJson.LoadFailure is not null) { - return Enumerable.Repeat(new GeneralStateTest { Name = name, LoadFailure = testJson.LoadFailure }, 1); + return Enumerable.Repeat(new GeneralStateTest { Name = name, Category = category, LoadFailure = testJson.LoadFailure }, 1); } List blockchainTests = new(); @@ -249,6 +249,7 @@ public static IEnumerable Convert(string name, GeneralStateTes { test.Name += testJson.Info?.Labels?[iterationNumber.ToString()]?.Replace(":label ", string.Empty); } + test.Category = category; test.ForkName = postStateBySpec.Key; test.Fork = ParseSpec(postStateBySpec.Key); @@ -278,15 +279,16 @@ public static IEnumerable Convert(string name, GeneralStateTes return blockchainTests; } - public static BlockchainTest Convert(string name, BlockchainTestJson testJson) + public static BlockchainTest Convert(string name, string category, BlockchainTestJson testJson) { if (testJson.LoadFailure is not null) { - return new BlockchainTest { Name = name, LoadFailure = testJson.LoadFailure }; + return new BlockchainTest { Name = name, Category = category, LoadFailure = testJson.LoadFailure }; } BlockchainTest test = new(); test.Name = name; + test.Category = category; test.Network = testJson.EthereumNetwork; test.NetworkAfterTransition = testJson.EthereumNetworkAfterTransition; test.TransitionForkActivation = testJson.TransitionForkActivation; @@ -318,9 +320,7 @@ public static IEnumerable ConvertToEofTests(string json) List tests = new(); foreach (KeyValuePair namedTest in testsInFile) { - var index = namedTest.Key.IndexOf(".py::"); - var name = namedTest.Key.Substring(index + 5); - string category = namedTest.Key.Substring(0, index).Replace("tests/osaka/eip7692_eof_v1/", ""); + (string name, string category) = GetNameAndCategory(namedTest.Key); string? description = null; string? url = null; @@ -375,7 +375,8 @@ public static IEnumerable ConvertStateTest(string json) List tests = new(); foreach (KeyValuePair namedTest in testsInFile) { - tests.AddRange(Convert(namedTest.Key, namedTest.Value)); + (string name, string category) = GetNameAndCategory(namedTest.Key); + tests.AddRange(Convert(name, category, namedTest.Value)); } return tests; @@ -411,10 +412,38 @@ public static IEnumerable ConvertToBlockchainTests(string json) testSpec.EthereumNetworkAfterTransition = ParseSpec(networks[1]); } - testsByName.Add(Convert(testName, testSpec)); + (string name, string category) = GetNameAndCategory(testName); + testsByName.Add(Convert(name, category, testSpec)); } return testsByName; } + + private static (string name, string category) GetNameAndCategory(string key) + { + key = key.Replace('\\', '/'); + var index = key.IndexOf(".py::"); + var name = key.Substring(index + 5); + string category = key.Substring(0, index); + int startIndex = 0; + for (var i = 0; i < 3; i++) + { + int newIndex = category.IndexOf("/", startIndex); + if (index < 0) + { + break; + } + if (index + 1 < category.Length) + { + startIndex = index + 1; + } + else + { + break; + } + } + category = category.Substring(startIndex); + return (name, category); + } } } From d578bb3caab9ed37220557e4313cd3bcab7054b5 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 28 Jan 2025 18:55:32 +0000 Subject: [PATCH 171/255] Fix test discovery --- src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index acd23c57ce5..2ef625a5b8c 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -423,6 +423,10 @@ private static (string name, string category) GetNameAndCategory(string key) { key = key.Replace('\\', '/'); var index = key.IndexOf(".py::"); + if (index < 0) + { + return (key, ""); + } var name = key.Substring(index + 5); string category = key.Substring(0, index); int startIndex = 0; From b8f4e44c88baa5dee20fd6e82869de3d4011599a Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 29 Jan 2025 04:18:20 +0000 Subject: [PATCH 172/255] naming --- src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 2ef625a5b8c..77c9fbd8499 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -439,7 +439,7 @@ private static (string name, string category) GetNameAndCategory(string key) } if (index + 1 < category.Length) { - startIndex = index + 1; + startIndex = newIndex + 1; } else { From 57d0c283ae9444f008ab228c490e28d8c14e6192 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 29 Jan 2025 11:27:54 +0000 Subject: [PATCH 173/255] Make non devnet-6 tests explicit --- .../Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs | 1 + .../Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs index c4b8e1a7512..4e6cfbc4c95 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaBlockChainTests.cs @@ -11,6 +11,7 @@ namespace Ethereum.Blockchain.Pyspec.Test; [TestFixture] [Parallelizable(ParallelScope.All)] +[Explicit("These tests are not yet updated to devnet-6")] public class OsakaBlockChainTests : BlockchainTestBase { [TestCaseSource(nameof(LoadTests))] diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs index 315a9a3454d..7537f077c2d 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs @@ -11,6 +11,7 @@ namespace Ethereum.Blockchain.Pyspec.Test; [TestFixture] [Parallelizable(ParallelScope.All)] +[Explicit("These tests are not yet updated to devnet-6")] public class OsakaStateTests : GeneralStateTestBase { [TestCaseSource(nameof(LoadTests))] From d0617c84ac1e13604137512235a85ee678bdf7d0 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 29 Jan 2025 18:55:59 -0700 Subject: [PATCH 174/255] Add EOF tracing fields (#8127) --- .../Nethermind.Evm.Test/EvmPooledMemoryTests.cs | 2 +- src/Nethermind/Nethermind.Evm/EvmState.cs | 1 + .../Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs | 2 +- .../Nethermind.Evm/Tracing/BlockReceiptsTracer.cs | 4 ++-- .../Nethermind.Evm/Tracing/CancellationTxTracer.cs | 4 ++-- .../Nethermind.Evm/Tracing/CompositeTxTracer.cs | 4 ++-- .../Nethermind.Evm/Tracing/Debugger/DebugTracer.cs | 4 ++-- .../Custom/JavaScript/GethLikeJavaScriptTxTracer.cs | 4 +++- .../Custom/Native/FourByte/Native4ByteTracer.cs | 2 +- .../Custom/Native/Prestate/NativePrestateTracer.cs | 4 ++-- .../Tracing/GethStyle/GethLikeTxMemoryTracer.cs | 4 ++-- .../Tracing/GethStyle/GethLikeTxTracer.cs | 4 +++- src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs | 4 +++- src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs | 2 +- .../Tracing/ParityStyle/ParityLikeTxTracer.cs | 4 +++- src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 8 +++++--- .../Nethermind.Test.Runner/StateTestTxTracer.cs | 4 +++- .../Nethermind.Test.Runner/StateTxTraceEntry.cs | 10 +++++++++- 19 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index 121febab4d3..32c21e6d1fd 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -242,7 +242,7 @@ public void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, { } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { } diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index 2ca2694c300..7a00f07c306 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -127,6 +127,7 @@ private void Initialize( ReturnStackHead = 0; ExecutionType = executionType; ProgramCounter = 0; + FunctionIndex = 0; IsTopLevel = isTopLevel; _canRestore = !isTopLevel; IsStatic = isStatic; diff --git a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs index c11d9fc01aa..c3191176534 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs @@ -48,7 +48,7 @@ public static AlwaysCancelTxTracer Instance public void MarkAsFailed(Address recipient, 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) => 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 ReportOperationError(EvmExceptionType error) => throw new OperationCanceledException(ErrorMessage); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs index 50e2e94de88..40ae1100f97 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs @@ -96,8 +96,8 @@ protected virtual TxReceipt BuildReceipt(Address recipient, long spentGas, byte return txReceipt; } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) => - _currentTxTracer.StartOperation(pc, opcode, gas, env); + 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 ReportOperationError(EvmExceptionType error) => _currentTxTracer.ReportOperationError(error); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs index 958d1ed496f..902de2b7b7d 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, GasConsumed gasSpent, byte[] output, } } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { token.ThrowIfCancellationRequested(); if (innerTracer.IsTracingInstructions) { - innerTracer.StartOperation(pc, opcode, gas, env); + innerTracer.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs index 70b95154b9d..3bba120f8c8 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -151,14 +151,14 @@ public void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, } } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { for (int index = 0; index < _txTracers.Count; index++) { ITxTracer innerTracer = _txTracers[index]; if (innerTracer.IsTracingInstructions) { - innerTracer.StartOperation(pc, opcode, gas, env); + innerTracer.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); } } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs index 332cf15c240..f9dd1a24bc0 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs @@ -194,8 +194,8 @@ public void MarkAsSuccess(Address recipient, GasConsumed gasSpent, byte[] output public void MarkAsFailed(Address recipient, 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) - => InnerTracer.StartOperation(pc, opcode, gas, env); + 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 ReportOperationError(EvmExceptionType error) => InnerTracer.ReportOperationError(error); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs index 64148a6bcf8..a383e9aedf0 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs @@ -100,7 +100,7 @@ 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) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { _log.pc = pc + env.CodeInfo.PcOffset(); _log.op = new Log.Opcode(opcode); @@ -108,6 +108,8 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe _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.Evm/Tracing/GethStyle/Custom/Native/FourByte/Native4ByteTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/FourByte/Native4ByteTracer.cs index 4397469959a..556cf2433df 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/FourByte/Native4ByteTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/FourByte/Native4ByteTracer.cs @@ -59,7 +59,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) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { _op = opcode; } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/Prestate/NativePrestateTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/Prestate/NativePrestateTracer.cs index c0b3915d230..90bd922a47f 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/Prestate/NativePrestateTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/Prestate/NativePrestateTracer.cs @@ -88,9 +88,9 @@ public override void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[ ProcessDiffState(); } - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { - base.StartOperation(pc, opcode, gas, env); + base.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); if (_error is not null) return; diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs index 7b1bf902e3f..d7f7315aebd 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs @@ -35,12 +35,12 @@ public override void SetOperationStorage(Address address, UInt256 storageIndex, .ToHexString(false); } - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { var previousTraceEntry = CurrentTraceEntry; var previousDepth = CurrentTraceEntry?.Depth ?? 0; - base.StartOperation(pc, opcode, gas, env); + base.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); if (CurrentTraceEntry.Depth > previousDepth) { diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs index 6ab13cdf9a1..d11d749a15c 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs @@ -69,7 +69,7 @@ public override void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[ protected GethLikeTxTracer(GethTraceOptions options) : base(options) { } private bool _gasCostAlreadySetForCurrentOp; - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { bool isPostMerge = env.IsPostMerge(); if (CurrentTraceEntry is not null) @@ -80,6 +80,8 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe CurrentTraceEntry.Gas = gas; CurrentTraceEntry.Opcode = opcode.GetName(isPostMerge); CurrentTraceEntry.ProgramCounter = pc; + // skip codeSection + // skip functionDepth _gasCostAlreadySetForCurrentOp = false; } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs index 8e038a6691b..1e47555c37e 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs @@ -179,8 +179,10 @@ public interface ITxTracer : IWorldStateTracer, IDisposable /// /// /// + /// + /// /// Depends on - void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env); + void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0); /// /// diff --git a/src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs index b8d3f6613a9..130c9079ac4 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/NullTxTracer.cs @@ -27,7 +27,7 @@ public override void MarkAsSuccess(Address recipient, GasConsumed gasSpent, byte => ThrowInvalidOperationException(); public override void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, string? error, Hash256? stateRoot = null) => ThrowInvalidOperationException(); - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) => ThrowInvalidOperationException(); public override void ReportOperationError(EvmExceptionType error) diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs index d5f690c1522..5d695fbd1a2 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs @@ -217,12 +217,14 @@ public override void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[ }; } - public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { ParityVmOperationTrace operationTrace = new(); _gasAlreadySetForCurrentOp = false; operationTrace.Pc = pc + env.CodeInfo.PcOffset(); operationTrace.Cost = gas; + // skip codeSection + // skip functionDepth _currentOperation = operationTrace; _currentPushList.Clear(); _currentVmTrace.Ops.Add(operationTrace); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index 8676caed544..01edc4af4c4 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs @@ -53,7 +53,7 @@ public virtual void ReportStorageChange(in StorageCell storageCell, byte[] befor public virtual void ReportStorageRead(in StorageCell storageCell) { } public virtual void MarkAsSuccess(Address recipient, GasConsumed gasSpent, byte[] output, LogEntry[] logs, Hash256? stateRoot = null) { } public virtual void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, string? error, Hash256? stateRoot = null) { } - public virtual void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) { } + public virtual void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { } 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/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index a2df1e7eae3..eb25a7a75b8 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -827,7 +827,7 @@ private CallResult ExecuteCode bytes; @@ -3474,10 +3474,12 @@ private static bool Jump(CodeInfo codeinfo, in UInt256 jumpDest, ref int program } [MethodImpl(MethodImplOptions.NoInlining)] - private void StartInstructionTrace(Instruction instruction, EvmState vmState, long gasAvailable, int programCounter, in EvmStack stackValue) + private void StartInstructionTrace(Instruction instruction, EvmState vmState, long gasAvailable, int programCounter, int sectionIndex, in EvmStack stackValue) where TIsTracing : struct, IIsTracing { - _txTracer.StartOperation(programCounter, instruction, gasAvailable, vmState.Env); + bool isEofFrame = vmState.Env.CodeInfo.Version > 0; + _txTracer.StartOperation(programCounter, instruction, gasAvailable, vmState.Env, + isEofFrame ? sectionIndex : 0, isEofFrame ? vmState.ReturnStackHead + 1 : 0); if (_txTracer.IsTracingMemory) { _txTracer.SetOperationMemory(vmState.Memory.GetTrace()); diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs index 6f3c2f129ea..653846533d5 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs @@ -50,16 +50,18 @@ public void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, _trace.Result.GasUsed = gasSpent; } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env) + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { bool isPostMerge = env.IsPostMerge(); _gasAlreadySetForCurrentOp = false; _traceEntry = new StateTestTxTraceEntry(); _traceEntry.Pc = pc + env.CodeInfo.PcOffset(); + _traceEntry.Section = codeSection; _traceEntry.Operation = (byte)opcode; _traceEntry.OperationName = opcode.GetName(isPostMerge); _traceEntry.Gas = gas; _traceEntry.Depth = env.GetGethTraceDepth(); + _traceEntry.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 4b384c06e5a..266fda53b27 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs @@ -16,6 +16,10 @@ public StateTestTxTraceEntry() [JsonPropertyName("pc")] public int Pc { get; set; } + [JsonPropertyName("section")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int Section { get; set; } + [JsonPropertyName("op")] public byte Operation { get; set; } @@ -37,10 +41,14 @@ public StateTestTxTraceEntry() [JsonPropertyName("depth")] public int Depth { get; set; } + [JsonPropertyName("functionDepth")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int FunctionDepth { get; set; } + [JsonPropertyName("refund")] public int Refund { get; set; } - [JsonPropertyName("opname")] + [JsonPropertyName("opName")] public string? OperationName { get; set; } [JsonPropertyName("error")] From 4d3ba974c0804765f52b16c3437b0acedbbffeff Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 22 Jul 2024 10:48:27 +0100 Subject: [PATCH 175/255] Refactor Evm --- .../Nethermind.Core/Specs/IReleaseSpec.cs | 2 + .../Specs/ReleaseSpecDecorator.cs | 1 + .../Tracing/DebugTracerTests.cs | 2 +- .../Nethermind.Evm/BadInstructionException.cs | 10 + src/Nethermind/Nethermind.Evm/EvmException.cs | 3 +- .../EvmObjectFormat/EofCodeInfo.cs | 2 + .../EvmObjectFormat/EofCodeValidator.cs | 11 +- src/Nethermind/Nethermind.Evm/EvmStack.cs | 55 +- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 1 + .../Nethermind.Evm/IVirtualMachine.cs | 19 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 49 +- .../Instructions/EvmInstructions.Bitwise.cs | 64 + .../Instructions/EvmInstructions.Call.cs | 252 ++ .../Instructions/EvmInstructions.CodeCopy.cs | 162 + .../Instructions/EvmInstructions.Create.cs | 186 + .../EvmInstructions.Environment.cs | 307 ++ .../Instructions/EvmInstructions.Eof.cs | 703 ++++ .../Instructions/EvmInstructions.Extras.cs | 74 + .../Instructions/EvmInstructions.Jump.cs | 83 + .../EvmInstructions.Math1Param.cs | 43 + .../EvmInstructions.Math2Param.cs | 154 + .../EvmInstructions.Math3Param.cs | 49 + .../Instructions/EvmInstructions.Shifts.cs | 79 + .../Instructions/EvmInstructions.Stack.cs | 135 + .../Instructions/EvmInstructions.Storage.cs | 295 ++ .../Instructions/EvmInstructions.cs | 525 +++ src/Nethermind/Nethermind.Evm/StackPool.cs | 25 - .../TransactionProcessor.cs | 4 +- .../Nethermind.Evm/VirtualMachine.cs | 2997 ++--------------- .../Simulate/SimulateVirtualMachine.cs | 8 +- .../EthereumJsonSerializer.cs | 1 + .../OverridableReleaseSpec.cs | 2 + .../Nethermind.Specs/ReleaseSpec.cs | 1 + 33 files changed, 3512 insertions(+), 2792 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/BadInstructionException.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index 51ea9d432e0..48b29624cc1 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -437,5 +437,7 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec bool IsAuthorizationListEnabled => IsEip7702Enabled; public bool RequestsEnabled => ConsolidationRequestsEnabled || WithdrawalRequestsEnabled || DepositsEnabled; + + public object? EvmInstructions { get; set; } } } diff --git a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs index d6204ca8b2c..455cc90de3a 100644 --- a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs +++ b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs @@ -139,4 +139,5 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec public virtual Address? FeeCollector => spec.FeeCollector; public virtual UInt256? Eip1559BaseFeeMinValue => spec.Eip1559BaseFeeMinValue; public virtual bool ValidateReceipts => spec.ValidateReceipts; + public object? EvmInstructions { get => spec.EvmInstructions; set => spec.EvmInstructions = value; } } diff --git a/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs b/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs index 33117398e36..bd428b212ee 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs @@ -276,7 +276,7 @@ public void Debugger_Can_Alter_Data_Stack(string bytecodeHex) if (tracer.CanReadState) { // we pop the condition and overwrite it with a false to force breaking out of the loop - EvmStack stack = new(tracer.CurrentState.DataStackHead, tracer, tracer.CurrentState.DataStack); + EvmStack stack = new(tracer.CurrentState.DataStackHead, tracer, tracer.CurrentState.DataStack); if (!stack.PopLimbo()) throw new EvmStackUnderflowException(); stack.PushByte(0x00); diff --git a/src/Nethermind/Nethermind.Evm/BadInstructionException.cs b/src/Nethermind/Nethermind.Evm/BadInstructionException.cs new file mode 100644 index 00000000000..e4ed6a181ce --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/BadInstructionException.cs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Evm +{ + public class BadInstructionException : EvmException + { + public override EvmExceptionType ExceptionType => EvmExceptionType.BadInstruction; + } +} diff --git a/src/Nethermind/Nethermind.Evm/EvmException.cs b/src/Nethermind/Nethermind.Evm/EvmException.cs index 2849a6dd796..7a32f3f0734 100644 --- a/src/Nethermind/Nethermind.Evm/EvmException.cs +++ b/src/Nethermind/Nethermind.Evm/EvmException.cs @@ -12,7 +12,8 @@ public abstract class EvmException : Exception public enum EvmExceptionType { - None, + Stop = -1, + None = 0, BadInstruction, StackOverflow, StackUnderflow, diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index cf3e51e0802..49546abcf2b 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -36,6 +36,8 @@ public class EofCodeInfo : ICodeInfo ); } + public bool ValidateJump(int destination) => true; + public EofCodeInfo(in EofContainer container) { EofContainer = container; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index 17ebd80b37b..b816fb940da 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -35,10 +35,18 @@ static EofValidator() /// 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 + 1) { - version = container.ByteAt(MAGIC.Length); + version = container[MAGIC.Length]; return container.StartsWith(MAGIC); } else @@ -46,7 +54,6 @@ public static bool IsEof(ReadOnlyMemory container, [NotNullWhen(true)] out version = 0; return false; } - } public static bool IsValidEofHeader(ReadOnlyMemory code, [NotNullWhen(true)] out EofHeader? header) diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 6f276238e3a..de01bd9f9df 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -16,25 +16,25 @@ namespace Nethermind.Evm; -using static VirtualMachine; using Word = Vector256; [StructLayout(LayoutKind.Auto)] -public ref struct EvmStack - where TTracing : struct, IIsTracing +public ref struct EvmStack { - public const int MaxStackSize = EvmStack.MaxStackSize; - public const int WordSize = EvmStack.WordSize; - public const int AddressSize = EvmStack.AddressSize; + 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; public EvmStack(scoped in int head, ITxTracer txTracer, scoped in Span bytes) { Head = head; - _tracer = txTracer; + _tracer = txTracer.IsTracingInstructions ? txTracer : null; _bytes = bytes; } - private readonly ITxTracer _tracer; + private readonly ITxTracer? _tracer; private readonly Span _bytes; public int Head; @@ -53,7 +53,7 @@ public ref byte PushBytesRef() public void PushBytes(scoped ReadOnlySpan value) { - if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value); + _tracer?.ReportStackPush(value); ref byte bytes = ref PushBytesRef(); if (value.Length != WordSize) @@ -70,7 +70,7 @@ public void PushBytes(scoped ReadOnlySpan value) public void PushBytes(scoped in ZeroPaddedSpan value) { - if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value); + _tracer?.ReportStackPush(value); ref byte bytes = ref PushBytesRef(); ReadOnlySpan valueSpan = value.Span; @@ -88,7 +88,7 @@ public void PushBytes(scoped in ZeroPaddedSpan value) public void PushLeftPaddedBytes(ReadOnlySpan value, int paddingLength) { - if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value); + _tracer?.ReportStackPush(value); ref byte bytes = ref PushBytesRef(); if (value.Length != WordSize) @@ -105,7 +105,7 @@ public void PushLeftPaddedBytes(ReadOnlySpan value, int paddingLength) public void PushByte(byte value) { - if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value); + _tracer?.ReportStackPush(value); ref byte bytes = ref PushBytesRef(); // Not full entry, clear first @@ -115,7 +115,7 @@ public void PushByte(byte value) public void PushOne() { - if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(Bytes.OneByteSpan); + _tracer?.ReportStackPush(Bytes.OneByteSpan); ref byte bytes = ref PushBytesRef(); // Not full entry, clear first @@ -125,7 +125,7 @@ public void PushOne() public void PushZero() { - if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(Bytes.ZeroByteSpan); + _tracer?.ReportStackPush(Bytes.ZeroByteSpan); ref byte bytes = ref PushBytesRef(); Unsafe.As(ref bytes) = default; @@ -140,7 +140,7 @@ public void PushUInt32(in int value) Span intPlace = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref bytes, WordSize - sizeof(uint)), sizeof(uint)); BinaryPrimitives.WriteInt32BigEndian(intPlace, value); - if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(intPlace); + _tracer?.ReportStackPush(intPlace); } /// @@ -192,7 +192,7 @@ public void PushUInt256(in UInt256 value) Unsafe.WriteUnaligned(ref bytes, Vector256.Create(u3, u2, u1, u0)); } - if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(MemoryMarshal.CreateReadOnlySpan(ref bytes, WordSize)); + _tracer?.ReportStackPush(MemoryMarshal.CreateReadOnlySpan(ref bytes, WordSize)); } public void PushSignedInt256(in Int256.Int256 value) @@ -359,7 +359,7 @@ public bool Dup(in int depth) Unsafe.WriteUnaligned(ref to, Unsafe.ReadUnaligned(ref from)); - if (typeof(TTracing) == typeof(IsTracing)) + if (_tracer is not null) { Trace(depth); } @@ -388,7 +388,7 @@ public readonly bool Swap(int depth) Unsafe.WriteUnaligned(ref bottom, Unsafe.ReadUnaligned(ref top)); Unsafe.WriteUnaligned(ref top, buffer); - if (typeof(TTracing) == typeof(IsTracing)) + if (_tracer is not null) { Trace(depth); } @@ -410,7 +410,7 @@ public readonly bool Exchange(int n, int m) Unsafe.WriteUnaligned(ref first, Unsafe.ReadUnaligned(ref second)); Unsafe.WriteUnaligned(ref second, buffer); - if (typeof(TTracing) == typeof(IsTracing)) + if (_tracer is not null) { Trace(maxDepth); } @@ -425,4 +425,21 @@ private readonly void Trace(int depth) _tracer.ReportStackPush(_bytes.Slice(Head * WordSize - i * WordSize, WordSize)); } } + + + [StackTraceHidden] + [DoesNotReturn] + internal static void ThrowEvmStackUnderflowException() + { + Metrics.EvmExceptions++; + throw new EvmStackUnderflowException(); + } + + [StackTraceHidden] + [DoesNotReturn] + internal static void ThrowEvmStackOverflowException() + { + Metrics.EvmExceptions++; + throw new EvmStackOverflowException(); + } } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index bf7f9b24224..eba187af25e 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -23,4 +23,5 @@ public interface ICodeInfo int PcOffset(); (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); void AnalyseInBackgroundIfRequired() { } + bool ValidateJump(int destination); } diff --git a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs index a5ca480a980..fb25cdb1230 100644 --- a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using Nethermind.Core.Specs; using Nethermind.Evm.Tracing; using Nethermind.State; @@ -10,7 +12,20 @@ namespace Nethermind.Evm { public interface IVirtualMachine { - TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) - where TTracingActions : struct, IIsTracing; + TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer); + } + + internal interface IEvm : IVirtualMachine + { + IReleaseSpec Spec { get; } + EvmState State { get; } + ITxTracer TxTracer { get; } + IWorldState WorldState { get; } + ReadOnlySpan ChainId { get; } + ICodeInfoRepository CodeInfoRepository { get; } + ReadOnlyMemory ReturnDataBuffer { get; set; } + IBlockhashProvider BlockhashProvider { get; } + int SectionIndex { get; set; } + object ReturnData { get; set; } } } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 3380e00e2ce..1a86013a006 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -86,6 +86,8 @@ public enum Instruction : byte MSIZE = 0x59, GAS = 0x5a, JUMPDEST = 0x5b, + TLOAD = 0x5c, // EIP-1153 + TSTORE = 0x5d, MCOPY = 0x5e, PUSH0 = 0x5f, // EIP-3855 @@ -162,20 +164,10 @@ public enum Instruction : byte LOG3 = 0xa3, LOG4 = 0xa4, - // EIP-1153 - TLOAD = 0x5c, - TSTORE = 0x5d, - - CREATE = 0xf0, - CALL = 0xf1, - CALLCODE = 0xf2, - RETURN = 0xf3, - DELEGATECALL = 0xf4, - CREATE2 = 0xf5, - STATICCALL = 0xfa, - REVERT = 0xfd, - INVALID = 0xfe, - SELFDESTRUCT = 0xff, + DATALOAD = 0xd0, + DATALOADN = 0xd1, + DATASIZE = 0xd2, + DATACOPY = 0xd3, RJUMP = 0xe0, RJUMPI = 0xe1, @@ -183,22 +175,30 @@ public enum Instruction : byte CALLF = 0xe3, RETF = 0xe4, JUMPF = 0xe5, - EOFCREATE = 0xec, - RETURNCONTRACT = 0xee, - DATALOAD = 0xd0, - DATALOADN = 0xd1, - DATASIZE = 0xd2, - DATACOPY = 0xd3, - DUPN = 0xe6, SWAPN = 0xe7, EXCHANGE = 0xe8, // random value opcode spec has collision - RETURNDATALOAD = 0xf7, - // opcode value not spec-ed + EOFCREATE = 0xec, + + RETURNCONTRACT = 0xee, + + + CREATE = 0xf0, + CALL = 0xf1, + CALLCODE = 0xf2, + RETURN = 0xf3, + DELEGATECALL = 0xf4, + CREATE2 = 0xf5, + RETURNDATALOAD = 0xf7, + // opcode value not spec-ed EXTCALL = 0xf8, EXTDELEGATECALL = 0xf9, // DelegateCallEnabled + STATICCALL = 0xfa, EXTSTATICCALL = 0xfb, // StaticCallEnabled + REVERT = 0xfd, + INVALID = 0xfe, + SELFDESTRUCT = 0xff, } public static class InstructionExtensions @@ -377,9 +377,6 @@ public static bool IsValid(this Instruction instruction, bool IsEofContext) spec ??= Frontier.Instance; return instruction switch { - Instruction.EXTCALL => "EXTCALL", - Instruction.EXTSTATICCALL => "EXTSTATICCALL", // StaticCallEnabled - Instruction.EXTDELEGATECALL => "EXTDELEGATECALL", Instruction.PREVRANDAO when !isPostMerge => "DIFFICULTY", Instruction.JUMPDEST => spec.IsEofEnabled ? "NOP" : "JUMPDEST", _ => FastEnum.IsDefined(instruction) ? FastEnum.GetName(instruction) : null, diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs new file mode 100644 index 00000000000..81cd0a32ac2 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using static System.Runtime.CompilerServices.Unsafe; + +namespace Nethermind.Evm; + +internal sealed partial class EvmInstructions +{ + public interface IOpBitwise + { + virtual static long GasCost => GasCostOf.VeryLow; + abstract static Vector256 Operation(Vector256 a, Vector256 b); + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionBitwise(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpBitwise : struct, IOpBitwise + { + gasAvailable -= TOpBitwise.GasCost; + + ref byte bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) return EvmExceptionType.StackUnderflow; + Vector256 aVec = ReadUnaligned>(ref bytesRef); + + bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) return EvmExceptionType.StackUnderflow; + Vector256 bVec = ReadUnaligned>(ref bytesRef); + + WriteUnaligned(ref stack.PushBytesRef(), TOpBitwise.Operation(aVec, bVec)); + + return EvmExceptionType.None; + } + + public struct OpBitwiseAnd : IOpBitwise + { + public static Vector256 Operation(Vector256 a, Vector256 b) => Vector256.BitwiseAnd(a, b); + } + + public struct OpBitwiseOr : IOpBitwise + { + public static Vector256 Operation(Vector256 a, Vector256 b) => Vector256.BitwiseOr(a, b); + } + + public struct OpBitwiseXor : IOpBitwise + { + public static Vector256 Operation(Vector256 a, Vector256 b) => Vector256.Xor(a, b); + } + + public struct OpBitwiseEq : IOpBitwise + { + public static Vector256 One = Vector256.Create( + (byte) + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 + ); + + public static Vector256 Operation(Vector256 a, Vector256 b) => a == b ? One : default; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs new file mode 100644 index 00000000000..252606881f9 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -0,0 +1,252 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Evm.CodeAnalysis; +using Nethermind.Int256; +using Nethermind.State; + +using static Nethermind.Evm.VirtualMachine; + +namespace Nethermind.Evm; + +internal sealed partial class EvmInstructions +{ + public interface IOpCall + { + virtual static bool IsStatic => false; + abstract static ExecutionType ExecutionType { get; } + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionCall(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpCall : struct, IOpCall + { + Metrics.IncrementCalls(); + + var spec = vm.Spec; + vm.ReturnData = null; + ref readonly ExecutionEnvironment env = ref vm.State.Env; + IWorldState state = vm.WorldState; + + if (!stack.PopUInt256(out UInt256 gasLimit)) return EvmExceptionType.StackUnderflow; + Address codeSource = stack.PopAddress(); + if (codeSource is null) return EvmExceptionType.StackUnderflow; + + if (!ChargeAccountAccessGas(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; + + UInt256 callValue; + if (typeof(TOpCall) == typeof(OpStaticCall)) + { + callValue = UInt256.Zero; + } + else if (typeof(TOpCall) == typeof(OpDelegateCall)) + { + callValue = env.Value; + } + else if (!stack.PopUInt256(out callValue)) + { + return EvmExceptionType.StackUnderflow; + } + + UInt256 transferValue = typeof(TOpCall) == typeof(OpDelegateCall) ? UInt256.Zero : callValue; + if (!stack.PopUInt256(out UInt256 dataOffset)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 dataLength)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 outputOffset)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 outputLength)) return EvmExceptionType.StackUnderflow; + + if (vm.State.IsStatic && !transferValue.IsZero && typeof(TOpCall) == typeof(OpCallCode)) return EvmExceptionType.StaticCallViolation; + + Address caller = typeof(TOpCall) == typeof(OpDelegateCall) ? env.Caller : env.ExecutingAccount; + Address target = typeof(TOpCall) == typeof(OpCall) || typeof(TOpCall) == typeof(OpStaticCall) + ? codeSource + : env.ExecutingAccount; + + //if (typeof(TLogger) == typeof(IsTracing)) + //{ + // TraceCallDetails(codeSource, ref callValue, ref transferValue, caller, target); + //} + + long gasExtra = 0L; + + if (!transferValue.IsZero) + { + gasExtra += GasCostOf.CallValue; + } + + if (!spec.ClearEmptyAccountWhenTouched && !state.AccountExists(target)) + { + gasExtra += GasCostOf.NewAccount; + } + else if (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && state.IsDeadAccount(target)) + { + gasExtra += GasCostOf.NewAccount; + } + + if (!UpdateGas(spec.GetCallCost(), ref gasAvailable) || + !UpdateMemoryCost(vm.State, ref gasAvailable, in dataOffset, dataLength) || + !UpdateMemoryCost(vm.State, ref gasAvailable, in outputOffset, outputLength) || + !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + ICodeInfo codeInfo = vm.CodeInfoRepository.GetCachedCodeInfo(state, codeSource, spec); + codeInfo.AnalyseInBackgroundIfRequired(); + + if (spec.Use63Over64Rule) + { + gasLimit = UInt256.Min((UInt256)(gasAvailable - gasAvailable / 64), gasLimit); + } + + if (gasLimit >= long.MaxValue) return EvmExceptionType.OutOfGas; + + long gasLimitUl = (long)gasLimit; + if (!UpdateGas(gasLimitUl, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + if (!transferValue.IsZero) + { + //if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportExtraGasPressure(GasCostOf.CallStipend); + gasLimitUl += GasCostOf.CallStipend; + } + + if (env.CallDepth >= MaxCallDepth || + !transferValue.IsZero && state.GetBalance(env.ExecutingAccount) < transferValue) + { + vm.ReturnDataBuffer = Array.Empty(); + stack.PushZero(); + + //if (typeof(TTracingInstructions) == typeof(IsTracing)) + //{ + // // very specific for Parity trace, need to find generalization - very peculiar 32 length... + // ReadOnlyMemory? memoryTrace = vm.State.Memory.Inspect(in dataOffset, 32); + // _txTracer.ReportMemoryChange(dataOffset, memoryTrace is null ? ReadOnlySpan.Empty : memoryTrace.Value.Span); + //} + + //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); + //if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationRemainingGas(gasAvailable); + //if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); + + UpdateGasUp(gasLimitUl, ref gasAvailable); + //if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportGasUpdateForVmTrace(gasLimitUl, gasAvailable); + return EvmExceptionType.None; + } + + Snapshot snapshot = state.TakeSnapshot(); + state.SubtractFromBalance(caller, transferValue, spec); + + if (codeInfo.IsEmpty /*&& typeof(TTracingInstructions) != typeof(IsTracing) && !_txTracer.IsTracingActions*/) + { + // Non contract call, no need to construct call frame can just credit balance and return gas + vm.ReturnDataBuffer = default; + stack.PushBytes(StatusCode.SuccessBytes.Span); + UpdateGasUp(gasLimitUl, ref gasAvailable); + return FastCall(vm, spec, in transferValue, target); + } + + ReadOnlyMemory callData = vm.State.Memory.Load(in dataOffset, dataLength); + ExecutionEnvironment callEnv = new + ( + txExecutionContext: in env.TxExecutionContext, + callDepth: env.CallDepth + 1, + caller: caller, + codeSource: codeSource, + executingAccount: target, + transferValue: transferValue, + value: callValue, + inputData: callData, + codeInfo: codeInfo + ); + //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Tx call gas {gasLimitUl}"); + if (outputLength == 0) + { + // TODO: when output length is 0 outputOffset can have any value really + // and the value does not matter and it can cause trouble when beyond long range + outputOffset = 0; + } + vm.ReturnData = EvmState.RentFrame( + gasLimitUl, + outputOffset.ToLong(), + outputLength.ToLong(), + TOpCall.ExecutionType, + TOpCall.IsStatic || vm.State.IsStatic, + isCreateOnPreExistingAccount: false, + snapshot: snapshot, + env: callEnv, + stateForAccessLists: vm.State.AccessTracker); + + return EvmExceptionType.None; + + EvmExceptionType FastCall(IEvm vm, IReleaseSpec spec, in UInt256 transferValue, Address target) + { + IWorldState state = vm.WorldState; + if (!state.AccountExists(target)) + { + state.CreateAccount(target, transferValue); + } + else + { + state.AddToBalance(target, transferValue, spec); + } + Metrics.IncrementEmptyCalls(); + + vm.ReturnData = null; + return EvmExceptionType.None; + } + + //[MethodImpl(MethodImplOptions.NoInlining)] + //void TraceCallDetails(Address codeSource, ref UInt256 callValue, ref UInt256 transferValue, Address caller, Address target) + //{ + // _logger.Trace($"caller {caller}"); + // _logger.Trace($"code source {codeSource}"); + // _logger.Trace($"target {target}"); + // _logger.Trace($"value {callValue}"); + // _logger.Trace($"transfer value {transferValue}"); + //} + } + + public struct OpCall : IOpCall + { + public static ExecutionType ExecutionType => ExecutionType.CALL; + } + + public struct OpCallCode : IOpCall + { + public static ExecutionType ExecutionType => ExecutionType.CALLCODE; + } + + public struct OpDelegateCall : IOpCall + { + public static ExecutionType ExecutionType => ExecutionType.DELEGATECALL; + } + + public struct OpStaticCall : IOpCall + { + public static bool IsStatic => true; + public static ExecutionType ExecutionType => ExecutionType.STATICCALL; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionReturn(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + if (vm.State.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) + { + return EvmExceptionType.BadInstruction; + } + + if (!stack.PopUInt256(out UInt256 position) || + !stack.PopUInt256(out UInt256 length)) + return EvmExceptionType.StackUnderflow; + + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in position, in length)) + { + return EvmExceptionType.OutOfGas; + } + + vm.ReturnData = vm.State.Memory.Load(in position, in length).ToArray(); + + return EvmExceptionType.None; + + + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs new file mode 100644 index 00000000000..4910f267225 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -0,0 +1,162 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using Nethermind.Core.Specs; +using Nethermind.Core; +using Nethermind.Evm.EvmObjectFormat; + +namespace Nethermind.Evm; +using Int256; + +internal sealed partial class EvmInstructions +{ + public interface IOpCodeCopy + { + abstract static ReadOnlySpan GetCode(IEvm vm); + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionCodeCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpCodeCopy : struct, IOpCodeCopy + { + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result); + + if (!result.IsZero) + { + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, result)) return EvmExceptionType.OutOfGas; + ZeroPaddedSpan slice = TOpCodeCopy.GetCode(vm).SliceWithZeroPadding(in b, (int)result); + vm.State.Memory.Save(in a, in slice); + if (vm.TxTracer.IsTracingInstructions) + { + vm.TxTracer.ReportMemoryChange(a, in slice); + } + } + + return EvmExceptionType.None; + } + + public struct OpCallDataCopy : IOpCodeCopy + { + public static ReadOnlySpan GetCode(IEvm vm) + => vm.State.Env.InputData.Span; + } + + public struct OpCodeCopy : IOpCodeCopy + { + public static ReadOnlySpan GetCode(IEvm vm) + => vm.State.Env.CodeInfo.MachineCode.Span; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionExtCodeCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + IReleaseSpec spec = vm.Spec; + Address address = stack.PopAddress(); + if (address is null) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + + gasAvailable -= spec.GetExtCodeCost() + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result); + + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + + if (!result.IsZero) + { + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, result)) return EvmExceptionType.OutOfGas; + + ReadOnlySpan externalCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, spec).MachineCode.Span; + if (spec.IsEofEnabled && EofValidator.IsEof(externalCode, out _)) + { + externalCode = EofValidator.MAGIC; + } + ZeroPaddedSpan slice = externalCode.SliceWithZeroPadding(in b, (int)result); + vm.State.Memory.Save(in a, in slice); + if (vm.TxTracer.IsTracingInstructions) + { + vm.TxTracer.ReportMemoryChange(a, in slice); + } + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionExtCodeSize(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + IReleaseSpec spec = vm.Spec; + gasAvailable -= spec.GetExtCodeCost(); + + Address address = stack.PopAddress(); + if (address is null) return EvmExceptionType.StackUnderflow; + + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + + var codeSection = vm.State.Env.CodeInfo.MachineCode.Span; + if (!vm.TxTracer.IsTracingInstructions && programCounter < codeSection.Length) + { + bool optimizeAccess = false; + Instruction nextInstruction = (Instruction)codeSection[programCounter]; + // code.length is zero + if (nextInstruction == Instruction.ISZERO) + { + optimizeAccess = true; + } + // code.length > 0 || code.length == 0 + else if ((nextInstruction == Instruction.GT || nextInstruction == Instruction.EQ) && + stack.PeekUInt256IsZero()) + { + optimizeAccess = true; + if (!stack.PopLimbo()) return EvmExceptionType.StackUnderflow; + } + + if (optimizeAccess) + { + // EXTCODESIZE ISZERO/GT/EQ peephole optimization. + // In solidity 0.8.1+: `return account.code.length > 0;` + // is is a common pattern to check if address is a contract + // however we can just check the address's loaded CodeHash + // to reduce storage access from trying to load the code + + programCounter++; + // Add gas cost for ISZERO, GT, or EQ + gasAvailable -= GasCostOf.VeryLow; + + // IsContract + bool isCodeLengthNotZero = vm.WorldState.IsContract(address); + if (nextInstruction == Instruction.GT) + { + // Invert, to IsNotContract + isCodeLengthNotZero = !isCodeLengthNotZero; + } + + if (!isCodeLengthNotZero) + { + stack.PushOne(); + } + else + { + stack.PushZero(); + } + return EvmExceptionType.None; + } + } + + ReadOnlySpan accountCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, spec).MachineCode.Span; + if (spec.IsEofEnabled && EofValidator.IsEof(accountCode, out _)) + { + stack.PushUInt256(2); + } + else + { + UInt256 result = (UInt256)accountCode.Length; + stack.PushUInt256(in result); + } + return EvmExceptionType.None; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs new file mode 100644 index 00000000000..e483068bb61 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Nethermind.Core; +using Nethermind.Evm.CodeAnalysis; +using Nethermind.Evm.EvmObjectFormat; +using Nethermind.Int256; +using Nethermind.State; + +using static Nethermind.Evm.VirtualMachine; + +namespace Nethermind.Evm; + +internal sealed partial class EvmInstructions +{ + public interface IOpCreate + { + abstract static ExecutionType ExecutionType { get; } + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionCreate(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpCreate : struct, IOpCreate + { + Metrics.IncrementCreates(); + + var spec = vm.Spec; + if (vm.State.IsStatic) return EvmExceptionType.StaticCallViolation; + + vm.ReturnData = null; + ref readonly ExecutionEnvironment env = ref vm.State.Env; + IWorldState state = vm.WorldState; + + // TODO: happens in CREATE_empty000CreateInitCode_Transaction but probably has to be handled differently + if (!state.AccountExists(env.ExecutingAccount)) + { + state.CreateAccount(env.ExecutingAccount, UInt256.Zero); + } + + if (!stack.PopUInt256(out UInt256 value) || + !stack.PopUInt256(out UInt256 memoryPositionOfInitCode) || + !stack.PopUInt256(out UInt256 initCodeLength)) + return EvmExceptionType.StackUnderflow; + + Span salt = default; + if (typeof(TOpCreate) == typeof(OpCreate2)) + { + salt = stack.PopWord256(); + } + + //EIP-3860 + if (spec.IsEip3860Enabled) + { + if (initCodeLength > spec.MaxInitCodeSize) return EvmExceptionType.OutOfGas; + } + + long gasCost = GasCostOf.Create + + (spec.IsEip3860Enabled ? GasCostOf.InitCodeWord * EvmPooledMemory.Div32Ceiling(in initCodeLength) : 0) + + (typeof(TOpCreate) == typeof(OpCreate2) + ? GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in initCodeLength) + : 0); + + if (!UpdateGas(gasCost, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in memoryPositionOfInitCode, in initCodeLength)) return EvmExceptionType.OutOfGas; + + // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? + if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients + { + // TODO: need a test for this + vm.ReturnDataBuffer = Array.Empty(); + stack.PushZero(); + return EvmExceptionType.None; + } + + ReadOnlyMemory initCode = vm.State.Memory.Load(in memoryPositionOfInitCode, in initCodeLength); + + UInt256 balance = state.GetBalance(env.ExecutingAccount); + if (value > balance) + { + vm.ReturnDataBuffer = Array.Empty(); + stack.PushZero(); + return EvmExceptionType.None; + } + + UInt256 accountNonce = state.GetNonce(env.ExecutingAccount); + UInt256 maxNonce = ulong.MaxValue; + if (accountNonce >= maxNonce) + { + vm.ReturnDataBuffer = Array.Empty(); + stack.PushZero(); + return EvmExceptionType.None; + } + + //if (vm.TxTracer.IsTracingInstructions) EndInstructionTrace(gasAvailable, vmState.Memory.Size); + // todo: === below is a new call - refactor / move + + long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; + if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + Address contractAddress = typeof(TOpCreate) == typeof(OpCreate) + ? ContractAddress.From(env.ExecutingAccount, state.GetNonce(env.ExecutingAccount)) + : ContractAddress.From(env.ExecutingAccount, salt, initCode.Span); + + if (spec.UseHotAndColdStorage) + { + // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 + vm.State.AccessTracker.WarmUp(contractAddress); + } + + // Do not add the initCode to the cache as it is + // pointing to data in this tx and will become invalid + // for another tx as returned to pool. + if (spec.IsEofEnabled && initCode.Span.StartsWith(EofValidator.MAGIC)) + { + vm.ReturnDataBuffer = Array.Empty(); + stack.PushZero(); + UpdateGasUp(callGas, ref gasAvailable); + return EvmExceptionType.None; + } + + state.IncrementNonce(env.ExecutingAccount); + + CodeInfoFactory.CreateInitCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, out _); + codeinfo.AnalyseInBackgroundIfRequired(); + + Snapshot snapshot = state.TakeSnapshot(); + + bool accountExists = state.AccountExists(contractAddress); + + if (accountExists && contractAddress.IsNonZeroAccount(spec, vm.CodeInfoRepository, state)) + { + /* we get the snapshot before this as there is a possibility with that we will touch an empty account and remove it even if the REVERT operation follows */ + //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); + vm.ReturnDataBuffer = Array.Empty(); + stack.PushZero(); + return EvmExceptionType.None; + } + + if (state.IsDeadAccount(contractAddress)) + { + state.ClearStorage(contractAddress); + } + + state.SubtractFromBalance(env.ExecutingAccount, value, spec); + + ExecutionEnvironment callEnv = new + ( + txExecutionContext: in env.TxExecutionContext, + callDepth: env.CallDepth + 1, + caller: env.ExecutingAccount, + executingAccount: contractAddress, + codeSource: null, + codeInfo: codeinfo, + inputData: default, + transferValue: value, + value: value + ); + vm.ReturnData = EvmState.RentFrame( + callGas, + outputDestination: 0, + outputLength: 0, + TOpCreate.ExecutionType, + isStatic: vm.State.IsStatic, + isCreateOnPreExistingAccount: accountExists, + in snapshot, + env: in callEnv, + in vm.State.AccessTracker + ); + + return EvmExceptionType.None; + } + + public struct OpCreate : IOpCreate + { + public static ExecutionType ExecutionType => ExecutionType.CREATE; + } + + public struct OpCreate2 : IOpCreate + { + public static ExecutionType ExecutionType => ExecutionType.CREATE2; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs new file mode 100644 index 00000000000..ac198101d88 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -0,0 +1,307 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Nethermind.Core.Specs; +using Nethermind.Core; +using Nethermind.Evm.EvmObjectFormat; +using Nethermind.Evm.Precompiles; +using Nethermind.State; + +using static Nethermind.Evm.VirtualMachine; + +namespace Nethermind.Evm; +using Int256; + +internal sealed partial class EvmInstructions +{ + [SkipLocalsInit] + public static EvmExceptionType InstructionPop(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Base; + stack.PopLimbo(); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionChainId(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + if (!vm.Spec.ChainIdOpcodeEnabled) return EvmExceptionType.BadInstruction; + + gasAvailable -= GasCostOf.Base; + stack.PushBytes(vm.ChainId); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionBalance(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + IReleaseSpec spec = vm.Spec; + gasAvailable -= spec.GetBalanceCost(); + + Address address = stack.PopAddress(); + if (address is null) return EvmExceptionType.StackUnderflow; + + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + + UInt256 result = vm.WorldState.GetBalance(address); + stack.PushUInt256(in result); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionExtCodeHash(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + IReleaseSpec spec = vm.Spec; + gasAvailable -= spec.GetExtCodeHashCost(); + + Address address = stack.PopAddress(); + if (address is null) return EvmExceptionType.StackUnderflow; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + + IWorldState state = vm.WorldState; + if (state.IsDeadAccount(address)) + { + stack.PushZero(); + } + else + { + if (spec.IsEofEnabled) + { + Memory code = state.GetCode(address); + if (EofValidator.IsEof(code, out _)) + { + stack.PushBytes(EofHash256); + return EvmExceptionType.None; + } + } + + stack.PushBytes(state.GetCodeHash(address).Bytes); + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + EvmState vmState = vm.State; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) return EvmExceptionType.OutOfGas; + Span bytes = vmState.Memory.LoadSpan(in result); + if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange(result, bytes); + + stack.PushBytes(bytes); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMStore(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + + Span bytes = stack.PopWord256(); + EvmState vmState = vm.State; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) return EvmExceptionType.OutOfGas; + vmState.Memory.SaveWord(in result, bytes); + if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange((long)result, bytes); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMStore8(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + + byte data = stack.PopByte(); + EvmState vmState = vm.State; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in UInt256.One)) return EvmExceptionType.OutOfGas; + vmState.Memory.SaveByte(in result, data); + if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange(result, data); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionSelfBalance(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.SelfBalance; + + UInt256 result = vm.WorldState.GetBalance(vm.State.Env.ExecutingAccount); + stack.PushUInt256(in result); + + return EvmExceptionType.None; + } + + public static bool ChargeAccountAccessGas(ref long gasAvailable, IEvm vm, Address address, bool chargeForWarm = true) + { + bool result = true; + IReleaseSpec spec = vm.Spec; + if (spec.UseHotAndColdStorage) + { + EvmState vmState = vm.State; + if (vm.TxTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list + { + vmState.AccessTracker.WarmUp(address); + } + + if (vmState.AccessTracker.IsCold(address) && !address.IsPrecompile(spec)) + { + result = UpdateGas(GasCostOf.ColdAccountAccess, ref gasAvailable); + vmState.AccessTracker.WarmUp(address); + } + else if (chargeForWarm) + { + result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); + } + } + + return result; + } + + public interface IOpEnvBytes + { + virtual static long GasCost => GasCostOf.Base; + abstract static void Operation(EvmState vmState, out Span result); + } + public interface IOpEnvUInt256 + { + virtual static long GasCost => GasCostOf.Base; + abstract static void Operation(EvmState vmState, out UInt256 result); + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEnvBytes(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEnv : struct, IOpEnvBytes + { + gasAvailable -= TOpEnv.GasCost; + + TOpEnv.Operation(vm.State, out Span result); + + stack.PushBytes(result); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEnvUInt256(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEnv : struct, IOpEnvUInt256 + { + if (typeof(TOpEnv) == typeof(OpBaseFee) && !vm.Spec.BaseFeeEnabled) return EvmExceptionType.BadInstruction; + if (typeof(TOpEnv) == typeof(OpBlobBaseFee) && !vm.Spec.BlobBaseFeeEnabled) return EvmExceptionType.BadInstruction; + gasAvailable -= TOpEnv.GasCost; + + TOpEnv.Operation(vm.State, out UInt256 result); + + stack.PushUInt256(in result); + + return EvmExceptionType.None; + } + + public struct OpAddress : IOpEnvBytes + { + public static void Operation(EvmState vmState, out Span result) + => result = vmState.Env.ExecutingAccount.Bytes; + } + + public struct OpCaller : IOpEnvBytes + { + public static void Operation(EvmState vmState, out Span result) + => result = vmState.Env.Caller.Bytes; + } + + public struct OpCallValue : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = vmState.Env.Value; + } + + public struct OpOrigin : IOpEnvBytes + { + public static void Operation(EvmState vmState, out Span result) + => result = vmState.Env.TxExecutionContext.Origin.Bytes; + } + + public struct OpCallDataSize : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = (UInt256)vmState.Env.InputData.Length; + } + + public struct OpCodeSize : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = (UInt256)vmState.Env.CodeInfo.MachineCode.Length; + } + + public struct OpGasPrice : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = vmState.Env.TxExecutionContext.GasPrice; + } + + public struct OpCoinbase : IOpEnvBytes + { + public static void Operation(EvmState vmState, out Span result) + => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasBeneficiary.Bytes; + } + + public struct OpTimestamp : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp; + } + + public struct OpNumber : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = (UInt256)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Number; + } + + public struct OpGasLimit : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = (UInt256)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasLimit; + } + + public struct OpBaseFee : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.BaseFeePerGas; + } + + public struct OpBlobBaseFee : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + { + UInt256? blobBaseFee = vmState.Env.TxExecutionContext.BlockExecutionContext.BlobBaseFee; + if (!blobBaseFee.HasValue) ThrowBadInstruction(); + + result = blobBaseFee.Value; + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowBadInstruction() => throw new BadInstructionException(); + } + } + + public struct OpMSize : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = vmState.Memory.Size; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs new file mode 100644 index 00000000000..d1df25d4592 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -0,0 +1,703 @@ +// SPDX-FileCopyrightText: 2022 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.Evm.CodeAnalysis; +using Nethermind.State; + +namespace Nethermind.Evm; +using Int256; + +using Nethermind.Evm.EvmObjectFormat; +using Nethermind.Evm.EvmObjectFormat.Handlers; + +using static Nethermind.Evm.VirtualMachine; + +internal sealed partial class EvmInstructions +{ + [SkipLocalsInit] + public static EvmExceptionType InstructionReturnDataSize(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Base; + + UInt256 result = (UInt256)vm.ReturnDataBuffer.Length; + stack.PushUInt256(in result); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionReturnDataCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 c)) return EvmExceptionType.StackUnderflow; + gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in c); + + ReadOnlyMemory returnDataBuffer = vm.ReturnDataBuffer; + if (vm.State.Env.CodeInfo.Version == 0 && (UInt256.AddOverflow(c, b, out UInt256 result) || result > returnDataBuffer.Length)) + { + return EvmExceptionType.AccessViolation; + } + + if (!c.IsZero) + { + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, c)) return EvmExceptionType.OutOfGas; + ZeroPaddedSpan slice = returnDataBuffer.Span.SliceWithZeroPadding(b, (int)c); + vm.State.Memory.Save(in a, in slice); + if (vm.TxTracer.IsTracingInstructions) + { + vm.TxTracer.ReportMemoryChange(a, in slice); + } + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionDataLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.DataLoad, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + stack.PopUInt256(out var a); + ZeroPaddedSpan zpbytes = codeInfo.DataSection.SliceWithZeroPadding(a, 32); + stack.PushBytes(zpbytes); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionDataLoadN(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.DataLoadN, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + var offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + ZeroPaddedSpan zpbytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); + stack.PushBytes(zpbytes); + + programCounter += EofValidator.TWO_BYTE_LENGTH; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionDataSize(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.DataSize, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + stack.PushUInt32(codeInfo.DataSection.Length); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionDataCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + stack.PopUInt256(out UInt256 memOffset); + stack.PopUInt256(out UInt256 offset); + stack.PopUInt256(out UInt256 size); + + if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable)) + return EvmExceptionType.OutOfGas; + + if (!size.IsZero) + { + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in memOffset, size)) + return EvmExceptionType.OutOfGas; + ZeroPaddedSpan dataSectionSlice = codeInfo.DataSection.SliceWithZeroPadding(offset, (int)size); + vm.State.Memory.Save(in memOffset, dataSectionSlice); + if (vm.TxTracer.IsTracingInstructions) + { + vm.TxTracer.ReportMemoryChange(memOffset, dataSectionSlice); + } + } + + stack.PushUInt32(codeInfo.DataSection.Length); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionRelativeJump(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + short offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + programCounter += EofValidator.TWO_BYTE_LENGTH + offset; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionRelativeJumpIf(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.RJumpi, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + Span condition = stack.PopWord256(); + short offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + if (!condition.IsZero()) + { + programCounter += offset; + } + programCounter += EofValidator.TWO_BYTE_LENGTH; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionJumpTable(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + stack.PopUInt256(out var a); + var codeSection = codeInfo.CodeSection.Span; + + var count = codeSection[programCounter] + 1; + var immediates = (ushort)(count * EofValidator.TWO_BYTE_LENGTH + EofValidator.ONE_BYTE_LENGTH); + if (a < count) + { + int case_v = programCounter + EofValidator.ONE_BYTE_LENGTH + (int)a * EofValidator.TWO_BYTE_LENGTH; + int offset = codeSection.Slice(case_v, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + programCounter += offset; + } + programCounter += immediates; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionCallFunction(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.Callf, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + var codeSection = codeInfo.CodeSection.Span; + var index = (int)codeSection.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + (int inputCount, _, int maxStackHeight) = codeInfo.GetSectionMetadata(index); + + if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) + { + return EvmExceptionType.StackOverflow; + } + + if (vm.State.ReturnStackHead == Eof1.RETURN_STACK_MAX_HEIGHT) + return EvmExceptionType.InvalidSubroutineEntry; + + vm.State.ReturnStack[vm.State.ReturnStackHead++] = new EvmState.ReturnState + { + Index = vm.SectionIndex, + Height = stack.Head - inputCount, + Offset = programCounter + EofValidator.TWO_BYTE_LENGTH + }; + + vm.SectionIndex = index; + programCounter = codeInfo.CodeSectionOffset(index).Start; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionReturnFunction(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.Retf, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + (_, int outputCount, _) = codeInfo.GetSectionMetadata(vm.SectionIndex); + + var stackFrame = vm.State.ReturnStack[--vm.State.ReturnStackHead]; + vm.SectionIndex = stackFrame.Index; + programCounter = stackFrame.Offset; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionJumpFunction(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.Jumpf, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + var index = (int)codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + (int inputCount, _, int maxStackHeight) = codeInfo.GetSectionMetadata(index); + + if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) + { + return EvmExceptionType.StackOverflow; + } + vm.SectionIndex = index; + programCounter = codeInfo.CodeSectionOffset(index).Start; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionDupN(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + int imm = (int)codeInfo.CodeSection.Span[programCounter]; + stack.Dup(imm + 1); + + programCounter += 1; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionSwapN(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + int n = 1 + (int)codeInfo.CodeSection.Span[programCounter]; + if (!stack.Swap(n + 1)) return EvmExceptionType.StackUnderflow; + + programCounter += 1; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionExchange(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + var codeSection = codeInfo.CodeSection.Span; + int n = 1 + (int)(codeSection[programCounter] >> 0x04); + int m = 1 + (int)(codeSection[programCounter] & 0x0f); + + stack.Exchange(n + 1, m + n + 1); + + programCounter += 1; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEofCreate(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + Metrics.IncrementCreates(); + vm.ReturnData = null; + + var spec = vm.Spec; + var codeInfo = vm.State.Env.CodeInfo; + if (codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + if (vm.State.IsStatic) return EvmExceptionType.StaticCallViolation; + + ref readonly ExecutionEnvironment env = ref vm.State.Env; + EofCodeInfo container = env.CodeInfo as EofCodeInfo; + var currentContext = ExecutionType.EOFCREATE; + + // 1 - deduct TX_CREATE_COST gas + if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) + return EvmExceptionType.OutOfGas; + + var codeSection = codeInfo.CodeSection.Span; + // 2 - read immediate operand initcontainer_index, encoded as 8-bit unsigned value + int initcontainerIndex = codeSection[programCounter++]; + + // 3 - pop value, salt, input_offset, input_size from the operand stack + // no stack checks becaue EOF guarantees no stack undeflows + stack.PopUInt256(out UInt256 value); + stack.PopWord256(out Span salt); + stack.PopUInt256(out UInt256 dataOffset); + stack.PopUInt256(out UInt256 dataSize); + + // 4 - perform (and charge for) memory expansion using [input_offset, input_size] + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in dataOffset, dataSize)) return EvmExceptionType.OutOfGas; + + // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed + // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header + ReadOnlySpan initContainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initcontainerIndex).Value]; + // Eip3860 + if (spec.IsEip3860Enabled) + { + //if (!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) + // return (EvmExceptionType.OutOfGas, null); + if (initContainer.Length > spec.MaxInitCodeSize) return EvmExceptionType.OutOfGas; + } + + // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) + long numberOfWordsInInitCode = EvmPooledMemory.Div32Ceiling((UInt256)initContainer.Length); + long hashCost = GasCostOf.Sha3Word * numberOfWordsInInitCode; + if (!UpdateGas(hashCost, ref gasAvailable)) + return EvmExceptionType.OutOfGas; + + var state = vm.WorldState; + // 7 - check that current call depth is below STACK_DEPTH_LIMIT and that caller balance is enough to transfer value + // in case of failure return 0 on the stack, caller’s nonce is not updated and gas for initcode execution is not consumed. + UInt256 balance = state.GetBalance(env.ExecutingAccount); + if (env.CallDepth >= MaxCallDepth || value > balance) + { + // TODO: need a test for this + vm.ReturnDataBuffer = Array.Empty(); + stack.PushZero(); + return EvmExceptionType.None; + } + + // 8 - caller’s memory slice [input_offset:input_size] is used as calldata + Span calldata = vm.State.Memory.LoadSpan(dataOffset, dataSize); + + // 9 - execute the container and deduct gas for execution. The 63/64th rule from EIP-150 applies. + long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; + if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + // 10 - increment sender account’s nonce + 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 new_address as keccak256(0xff || sender || salt || keccak256(initcontainer))[12:] + Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initContainer); + if (spec.UseHotAndColdStorage) + { + // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 + vm.State.AccessTracker.WarmUp(contractAddress); + } + + + // if (vm.TxTracer.IsTracingInstructions) EndInstructionTrace(gasAvailable, vm.State?.Memory.Size ?? 0); + // todo: === below is a new call - refactor / move + + Snapshot snapshot = state.TakeSnapshot(); + + bool accountExists = state.AccountExists(contractAddress); + + if (accountExists && contractAddress.IsNonZeroAccount(spec, vm.CodeInfoRepository, state)) + { + /* we get the snapshot before this as there is a possibility with that we will touch an empty account and remove it even if the REVERT operation follows */ + //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); + vm.ReturnDataBuffer = Array.Empty(); + stack.PushZero(); + return EvmExceptionType.None; + } + + if (state.IsDeadAccount(contractAddress)) + { + state.ClearStorage(contractAddress); + } + + state.SubtractFromBalance(env.ExecutingAccount, value, spec); + + + ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initContainer.ToArray(), spec, EvmObjectFormat.ValidationStrategy.ExractHeader); + + ExecutionEnvironment callEnv = new + ( + txExecutionContext: in env.TxExecutionContext, + callDepth: env.CallDepth + 1, + caller: env.ExecutingAccount, + executingAccount: contractAddress, + codeSource: null, + codeInfo: codeinfo, + inputData: calldata.ToArray(), + transferValue: value, + value: value + ); + vm.ReturnData = EvmState.RentFrame( + callGas, + outputDestination: 0, + outputLength: 0, + executionType: currentContext, + isStatic: vm.State.IsStatic, + isCreateOnPreExistingAccount: accountExists, + in snapshot, + env: in callEnv, + in vm.State.AccessTracker + ); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionReturnContract(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + if (!vm.State.ExecutionType.IsAnyCreateEof()) + return EvmExceptionType.BadInstruction; + + + if (!UpdateGas(GasCostOf.ReturnContract, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + var spec = vm.Spec; + var codeInfo = vm.State.Env.CodeInfo; + + byte sectionIdx = codeInfo.CodeSection.Span[programCounter++]; + ReadOnlyMemory deployCode = codeInfo.ContainerSection[(Range)codeInfo.ContainerSectionOffset(sectionIdx)]; + EofCodeInfo deploycodeInfo = (EofCodeInfo)CodeInfoFactory.CreateCodeInfo(deployCode, spec, EvmObjectFormat.ValidationStrategy.ExractHeader); + + stack.PopUInt256(out var a); + stack.PopUInt256(out var b); + ReadOnlyMemory auxData = ReadOnlyMemory.Empty; + + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; + + int projectedNewSize = (int)b + deploycodeInfo.DataSection.Length; + if (projectedNewSize < deploycodeInfo.EofContainer.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) + { + return EvmExceptionType.AccessViolation; + } + + vm.ReturnDataBuffer = vm.State.Memory.Load(a, b); + vm.ReturnData = deploycodeInfo; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionReturnDataLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + var spec = vm.Spec; + var codeInfo = vm.State.Env.CodeInfo; + if (!spec.IsEofEnabled || codeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + + var slice = vm.ReturnDataBuffer.Span.SliceWithZeroPadding(a, 32); + stack.PushBytes(slice); + + return EvmExceptionType.None; + } + + public interface IOpEofCall + { + virtual static bool IsStatic => false; + abstract static ExecutionType ExecutionType { get; } + } + + public struct OpEofCall : IOpEofCall + { + public static ExecutionType ExecutionType => Evm.ExecutionType.EOFCALL; + } + + public struct OpEofDelegateCall : IOpEofCall + { + public static ExecutionType ExecutionType => Evm.ExecutionType.EOFDELEGATECALL; + } + + public struct OpEofStaticCall : IOpEofCall + { + public static bool IsStatic => true; + public static ExecutionType ExecutionType => Evm.ExecutionType.EOFSTATICCALL; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEofCall(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEofCall : struct, IOpEofCall + { + Metrics.IncrementCalls(); + + const int MIN_RETAINED_GAS = 5000; + + var spec = vm.Spec; + vm.ReturnData = null; + ref readonly ExecutionEnvironment env = ref vm.State.Env; + IWorldState state = vm.WorldState; + + // Instruction is undefined in legacy code and only available in EOF + if (env.CodeInfo.Version == 0) + return EvmExceptionType.BadInstruction; + + // 1. Pop required arguments from stack, halt with exceptional failure on stack underflow. + stack.PopWord256(out Span targetBytes); + stack.PopUInt256(out UInt256 dataOffset); + stack.PopUInt256(out UInt256 dataLength); + + UInt256 callValue; + if (typeof(TOpEofCall) == typeof(OpEofStaticCall)) + { + callValue = UInt256.Zero; + } + else if (typeof(TOpEofCall) == typeof(OpEofDelegateCall)) + { + callValue = env.Value; + } + else if (!stack.PopUInt256(out callValue)) + { + return EvmExceptionType.StackUnderflow; + } + + // 3. If value is non-zero: + // a: Halt with exceptional failure if the current frame is in static-mode. + if (vm.State.IsStatic && !callValue.IsZero) return EvmExceptionType.StaticCallViolation; + // b. Charge CALL_VALUE_COST gas. + if (!callValue.IsZero && !UpdateGas(GasCostOf.CallValue, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + // 4. If target_address has any of the high 12 bytes set to a non-zero value + // (i.e. it does not contain a 20-byte address) + if (!targetBytes[0..12].IsZero()) + { + // then halt with an exceptional failure. + return EvmExceptionType.AddressOutOfRange; + } + + Address caller = typeof(TOpEofCall) == typeof(OpEofDelegateCall) ? env.Caller : env.ExecutingAccount; + Address codeSource = new Address(targetBytes[12..].ToArray()); + Address target = typeof(TOpEofCall) == typeof(OpEofDelegateCall) + ? env.ExecutingAccount + : codeSource; + + // 5. Perform (and charge for) memory expansion using [input_offset, input_size]. + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in dataOffset, in dataLength)) return EvmExceptionType.OutOfGas; + // 1. Charge WARM_STORAGE_READ_COST (100) gas. + // 6. If target_address is not in the warm_account_list, charge COLD_ACCOUNT_ACCESS - WARM_STORAGE_READ_COST (2500) gas. + if (!ChargeAccountAccessGas(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; + + if ((!spec.ClearEmptyAccountWhenTouched && !state.AccountExists(codeSource)) + || (spec.ClearEmptyAccountWhenTouched && callValue != 0 && state.IsDeadAccount(codeSource))) + { + // 7. If target_address is not in the state and the call configuration would result in account creation, + // charge ACCOUNT_CREATION_COST (25000) gas. (The only such case in this EIP is if value is non-zero.) + if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; + } + + // 8. Calculate the gas available to callee as caller’s remaining gas reduced by max(floor(gas/64), MIN_RETAINED_GAS). + long callGas = gasAvailable - Math.Max(gasAvailable / 64, MIN_RETAINED_GAS); + + // 9. Fail with status code 1 returned on stack if any of the following is true (only gas charged until this point is consumed): + // a: Gas available to callee at this point is less than MIN_CALLEE_GAS. + // b: Balance of the current account is less than value. + // c: Current call stack depth equals 1024. + if (callGas < GasCostOf.CallStipend || + (!callValue.IsZero && state.GetBalance(env.ExecutingAccount) < callValue) || + env.CallDepth >= MaxCallDepth) + { + vm.ReturnData = null; + vm.ReturnDataBuffer = Array.Empty(); + stack.PushOne(); + + //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); + //if (_txTracer.IsTracingInstructions) + //{ + // // very specific for Parity trace, need to find generalization - very peculiar 32 length... + // ReadOnlyMemory memoryTrace = 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; + } + + //if (typeof(TLogger) == typeof(IsTracing)) + //{ + // _logger.Trace($"caller {caller}"); + // _logger.Trace($"target {codeSource}"); + // _logger.Trace($"value {callValue}"); + //} + + ICodeInfo targetCodeInfo = vm.CodeInfoRepository.GetCachedCodeInfo(state, codeSource, spec); + targetCodeInfo.AnalyseInBackgroundIfRequired(); + + if (typeof(TOpEofCall) == typeof(OpEofDelegateCall) + && targetCodeInfo.Version == 0) + { + // https://github.com/ipsilon/eof/blob/main/spec/eof.md#new-behavior + // EXTDELEGATECALL to a non-EOF contract (legacy contract, EOA, empty account) is disallowed, + // and it returns 1 (same as when the callee frame reverts) to signal failure. + // Only initial gas cost of EXTDELEGATECALL is consumed (similarly to the call depth check) + // and the target address still becomes warm. + vm.ReturnData = null; + vm.ReturnDataBuffer = Array.Empty(); + stack.PushOne(); + return EvmExceptionType.None; + } + + // 10. Perform the call with the available gas and configuration. + if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + ReadOnlyMemory callData = vm.State.Memory.Load(in dataOffset, dataLength); + + Snapshot snapshot = state.TakeSnapshot(); + state.SubtractFromBalance(caller, callValue, spec); + + ExecutionEnvironment callEnv = new + ( + txExecutionContext: in env.TxExecutionContext, + callDepth: env.CallDepth + 1, + caller: caller, + codeSource: codeSource, + executingAccount: target, + transferValue: callValue, + value: callValue, + inputData: callData, + codeInfo: targetCodeInfo + ); + //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Tx call gas {callGas}"); + vm.ReturnData = EvmState.RentFrame( + callGas, + outputDestination: 0, + outputLength: 0, + TOpEofCall.ExecutionType, + isStatic: TOpEofCall.IsStatic || vm.State.IsStatic, + isCreateOnPreExistingAccount: false, + in snapshot, + env: in callEnv, + in vm.State.AccessTracker + ); + + return EvmExceptionType.None; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs new file mode 100644 index 00000000000..b8c912c7dfd --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using Nethermind.Core.Specs; + +namespace Nethermind.Evm; +using Int256; + +using Nethermind.Core.Crypto; +using static Nethermind.Evm.VirtualMachine; + +internal sealed partial class EvmInstructions +{ + [SkipLocalsInit] + public static EvmExceptionType InstructionGas(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Base; + + // Ensure gas is positive before pushing to stack + if (gasAvailable < 0) return EvmExceptionType.OutOfGas; + + stack.PushUInt256((UInt256)gasAvailable); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionBlobHash(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + IReleaseSpec spec = vm.Spec; + if (!spec.IsEip4844Enabled) return EvmExceptionType.BadInstruction; + + gasAvailable -= GasCostOf.BlobHash; + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + + byte[][] versionedHashes = vm.State.Env.TxExecutionContext.BlobVersionedHashes; + + if (versionedHashes is not null && result < versionedHashes.Length) + { + stack.PushBytes(versionedHashes[result.u0]); + } + else + { + stack.PushZero(); + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionBlockHash(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + Metrics.BlockhashOpcode++; + + gasAvailable -= GasCostOf.BlockHash; + + if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + long number = a > long.MaxValue ? long.MaxValue : (long)a; + + Hash256? blockHash = vm.BlockhashProvider.GetBlockhash(vm.State.Env.TxExecutionContext.BlockExecutionContext.Header, number); + + stack.PushBytes(blockHash is not null ? blockHash.Bytes : BytesZero32); + + if (vm.TxTracer.IsTracingBlockHash && blockHash is not null) + { + vm.TxTracer.ReportBlockHash(blockHash); + } + + return EvmExceptionType.None; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs new file mode 100644 index 00000000000..cc22ca6fafe --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace Nethermind.Evm; +using Int256; + +internal sealed partial class EvmInstructions +{ + [SkipLocalsInit] + public static EvmExceptionType InstructionProgramCounter(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Base; + stack.PushUInt32(programCounter - 1); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionJumpDest(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.JumpDest; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionJump(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Mid; + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!Jump(result, ref programCounter, in vm.State.Env)) return EvmExceptionType.InvalidJumpDestination; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + [MethodImpl(MethodImplOptions.NoInlining)] + public static EvmExceptionType InstructionJumpIf(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.High; + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + ref byte condition = ref stack.PopBytesByRef(); + if (Unsafe.IsNullRef(in condition)) return EvmExceptionType.StackUnderflow; + if (Unsafe.As>(ref condition) != default) + { + if (!Jump(result, ref programCounter, in vm.State.Env)) return EvmExceptionType.InvalidJumpDestination; + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + private static bool Jump(in UInt256 jumpDestination, ref int programCounter, in ExecutionEnvironment env) + { + bool isJumpDestination = true; + if (jumpDestination > int.MaxValue) + { + // https://github.com/NethermindEth/nethermind/issues/140 + // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 0xf435a354924097686ea88dab3aac1dd464e6a3b387c77aeee94145b0fa5a63d2 mainnet + isJumpDestination = false; + } + else + { + int jumpDestinationInt = (int)jumpDestination.u0; + if (!env.CodeInfo.ValidateJump(jumpDestinationInt)) + { + // https://github.com/NethermindEth/nethermind/issues/140 + // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 61363 Ropsten + isJumpDestination = false; + } + else + { + programCounter = jumpDestinationInt; + } + } + + return isJumpDestination; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs new file mode 100644 index 00000000000..d69c826aa3e --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using static System.Runtime.CompilerServices.Unsafe; + +namespace Nethermind.Evm; + +internal sealed partial class EvmInstructions +{ + public interface IOpMath1Param + { + virtual static long GasCost => GasCostOf.VeryLow; + abstract static Vector256 Operation(ref byte bytesRef); + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMath1Param(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpMath : struct, IOpMath1Param + { + gasAvailable -= TOpMath.GasCost; + + ref byte bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) return EvmExceptionType.StackUnderflow; + + Vector256 result = TOpMath.Operation(ref bytesRef); + + WriteUnaligned(ref stack.PushBytesRef(), result); + + return EvmExceptionType.None; + } + + public struct OpNot : IOpMath1Param + { + public static Vector256 Operation(ref byte bytesRef) => Vector256.OnesComplement(ReadUnaligned>(ref bytesRef)); + } + + public struct OpIsZero : IOpMath1Param + { + public static Vector256 Operation(ref byte bytesRef) => As>(ref bytesRef) == default ? OpBitwiseEq.One : default; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs new file mode 100644 index 00000000000..80af332adde --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs @@ -0,0 +1,154 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Runtime.CompilerServices; +using static Nethermind.Evm.VirtualMachine; +using static System.Runtime.CompilerServices.Unsafe; + +namespace Nethermind.Evm; +using Int256; + +internal sealed partial class EvmInstructions +{ + public interface IOpMath2Param + { + virtual static long GasCost => GasCostOf.VeryLow; + abstract static void Operation(in UInt256 a, in UInt256 b, out UInt256 result); + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMath2Param(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpMath : struct, IOpMath2Param + { + gasAvailable -= TOpMath.GasCost; + + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + + TOpMath.Operation(in a, in b, out UInt256 result); + + stack.PushUInt256(in result); + + return EvmExceptionType.None; + } + + public struct OpAdd : IOpMath2Param + { + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) => UInt256.Add(in a, in b, out result); + } + + public struct OpSub : IOpMath2Param + { + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) => UInt256.Subtract(in a, in b, out result); + } + + public struct OpMul : IOpMath2Param + { + public static long GasCost => GasCostOf.Low; + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) => UInt256.Multiply(in a, in b, out result); + } + + public struct OpDiv : IOpMath2Param + { + public static long GasCost => GasCostOf.Low; + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + { + if (b.IsZero) + { + result = default; + } + else + { + UInt256.Divide(in a, in b, out result); + } + } + } + + public struct OpSDiv : IOpMath2Param + { + public static long GasCost => GasCostOf.Low; + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + { + if (b.IsZero) + { + result = default; + } + else if (As(ref AsRef(in b)) == Int256.MinusOne && a == P255) + { + result = P255; + } + else + { + SkipInit(out result); + Int256.Divide( + in As(ref AsRef(in a)), + in As(ref AsRef(in b)), + out As(ref result)); + } + } + } + + public struct OpMod : IOpMath2Param + { + public static long GasCost => GasCostOf.Low; + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) => UInt256.Mod(in a, in b, out result); + } + + public struct OpSMod : IOpMath2Param + { + public static long GasCost => GasCostOf.Low; + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + { + if (b.IsZeroOrOne) + { + result = default; + } + else + { + SkipInit(out result); + As(ref AsRef(in a)) + .Mod( + in As(ref AsRef(in b)), + out As(ref result)); + } + } + } + + public struct OpLt : IOpMath2Param + { + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + { + result = a < b ? UInt256.One : default; + } + } + + public struct OpGt : IOpMath2Param + { + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + { + result = a > b ? UInt256.One : default; + } + } + + public struct OpSLt : IOpMath2Param + { + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + { + result = As(ref AsRef(in a)) + .CompareTo(As(ref AsRef(in b))) < 0 ? + UInt256.One : + default; + } + } + + public struct OpSGt : IOpMath2Param + { + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + { + result = As(ref AsRef(in a)) + .CompareTo(As(ref AsRef(in b))) > 0 ? + UInt256.One : + default; + } + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs new file mode 100644 index 00000000000..2366a4b87ad --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Runtime.CompilerServices; + +namespace Nethermind.Evm; +using Int256; + +internal sealed partial class EvmInstructions +{ + public interface IOpMath3Param + { + virtual static long GasCost => GasCostOf.Mid; + abstract static void Operation(in UInt256 a, in UInt256 b, in UInt256 c, out UInt256 result); + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMath3Param(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpMath : struct, IOpMath3Param + { + gasAvailable -= TOpMath.GasCost; + + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 c)) return EvmExceptionType.StackUnderflow; + + if (c.IsZero) + { + stack.PushZero(); + } + else + { + TOpMath.Operation(in a, in b, in c, out UInt256 result); + stack.PushUInt256(in result); + } + + return EvmExceptionType.None; + } + + public struct OpAddMod : IOpMath3Param + { + public static void Operation(in UInt256 a, in UInt256 b, in UInt256 c, out UInt256 result) => UInt256.AddMod(in a, in b, in c, out result); + } + + public struct OpMulMod : IOpMath3Param + { + public static void Operation(in UInt256 a, in UInt256 b, in UInt256 c, out UInt256 result) => UInt256.MultiplyMod(in a, in b, in c, out result); + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs new file mode 100644 index 00000000000..8c17fb20931 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Runtime.CompilerServices; +using static System.Runtime.CompilerServices.Unsafe; + +namespace Nethermind.Evm; +using Int256; + +internal sealed partial class EvmInstructions +{ + public interface IOpShift + { + virtual static long GasCost => GasCostOf.VeryLow; + abstract static void Operation(in UInt256 a, in UInt256 b, out UInt256 result); + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionShift(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpShift : struct, IOpShift + { + gasAvailable -= TOpShift.GasCost; + + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (a >= 256) + { + stack.PopLimbo(); + stack.PushZero(); + } + else + { + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + TOpShift.Operation(in a, in b, out UInt256 result); + stack.PushUInt256(in result); + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionSar(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + + if (a >= 256) + { + if (As(ref b).Sign >= 0) + { + stack.PushZero(); + } + else + { + stack.PushSignedInt256(in Int256.MinusOne); + } + } + else + { + As(ref b).RightShift((int)a, out Int256 result); + stack.PushUInt256(in As(ref result)); + } + + return EvmExceptionType.None; + } + + public struct OpShl : IOpShift + { + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + => result = b << (int)a.u0; + } + + public struct OpShr : IOpShift + { + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + => result = b >> (int)a.u0; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs new file mode 100644 index 00000000000..5bd24cd09e8 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -0,0 +1,135 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using Nethermind.Core; +using Nethermind.Core.Crypto; + +namespace Nethermind.Evm; +using Int256; + +internal sealed partial class EvmInstructions +{ + public interface IOpCount + { + abstract static int Count { get; } + } + + public struct Op0 : IOpCount { public static int Count => 0; } + public struct Op1 : IOpCount { public static int Count => 1; } + public struct Op2 : IOpCount { public static int Count => 2; } + public struct Op3 : IOpCount { public static int Count => 3; } + public struct Op4 : IOpCount { public static int Count => 4; } + public struct Op5 : IOpCount { public static int Count => 5; } + public struct Op6 : IOpCount { public static int Count => 6; } + public struct Op7 : IOpCount { public static int Count => 7; } + public struct Op8 : IOpCount { public static int Count => 8; } + public struct Op9 : IOpCount { public static int Count => 9; } + public struct Op10 : IOpCount { public static int Count => 10; } + public struct Op11 : IOpCount { public static int Count => 11; } + public struct Op12 : IOpCount { public static int Count => 12; } + public struct Op13 : IOpCount { public static int Count => 13; } + public struct Op14 : IOpCount { public static int Count => 14; } + public struct Op15 : IOpCount { public static int Count => 15; } + public struct Op16 : IOpCount { public static int Count => 16; } + public struct Op17 : IOpCount { public static int Count => 17; } + public struct Op18 : IOpCount { public static int Count => 18; } + public struct Op19 : IOpCount { public static int Count => 19; } + public struct Op20 : IOpCount { public static int Count => 20; } + public struct Op21 : IOpCount { public static int Count => 21; } + public struct Op22 : IOpCount { public static int Count => 22; } + public struct Op23 : IOpCount { public static int Count => 23; } + public struct Op24 : IOpCount { public static int Count => 24; } + public struct Op25 : IOpCount { public static int Count => 25; } + public struct Op26 : IOpCount { public static int Count => 26; } + public struct Op27 : IOpCount { public static int Count => 27; } + public struct Op28 : IOpCount { public static int Count => 28; } + public struct Op29 : IOpCount { public static int Count => 29; } + public struct Op30 : IOpCount { public static int Count => 30; } + public struct Op31 : IOpCount { public static int Count => 31; } + public struct Op32 : IOpCount { public static int Count => 32; } + + [SkipLocalsInit] + public static EvmExceptionType InstructionDup(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpCount : IOpCount + { + gasAvailable -= GasCostOf.VeryLow; + if (!stack.Dup(TOpCount.Count)) return EvmExceptionType.StackUnderflow; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionSwap(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpCount : IOpCount + { + gasAvailable -= GasCostOf.VeryLow; + if (!stack.Swap(TOpCount.Count + 1)) return EvmExceptionType.StackUnderflow; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionPush0(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Base; + + stack.PushZero(); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionPush(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpCount : IOpCount + { + gasAvailable -= GasCostOf.VeryLow; + + ReadOnlySpan code = vm.State.Env.CodeInfo.CodeSection.Span; + + int length = TOpCount.Count; + int usedFromCode = Math.Min(code.Length - programCounter, length); + stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + + programCounter += length; + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionLog(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpCount : struct, IOpCount + { + EvmState vmState = vm.State; + if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; + + if (!stack.PopUInt256(out UInt256 position)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; + long topicsCount = TOpCount.Count; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, length)) return EvmExceptionType.OutOfGas; + if (!UpdateGas( + GasCostOf.Log + topicsCount * GasCostOf.LogTopic + + (long)length * GasCostOf.LogData, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + ReadOnlyMemory data = vmState.Memory.Load(in position, length); + Hash256[] topics = new Hash256[topicsCount]; + for (int i = 0; i < topicsCount; i++) + { + topics[i] = new Hash256(stack.PopWord256()); + } + + LogEntry logEntry = new( + vmState.Env.ExecutingAccount, + data.ToArray(), + topics); + vmState.AccessTracker.Logs.Add(logEntry); + + if (vm.TxTracer.IsTracingLogs) + { + vm.TxTracer.ReportLog(logEntry); + } + + return EvmExceptionType.None; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs new file mode 100644 index 00000000000..de06eae4143 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -0,0 +1,295 @@ +// SPDX-FileCopyrightText: 2022 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.Tracing; +using static Nethermind.Evm.VirtualMachine; + +namespace Nethermind.Evm; +using Int256; + + +internal sealed partial class EvmInstructions +{ + internal enum StorageAccessType + { + SLOAD, + SSTORE + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionTLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + Metrics.TloadOpcode++; + gasAvailable -= GasCostOf.TLoad; + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + StorageCell storageCell = new(vm.State.Env.ExecutingAccount, result); + + ReadOnlySpan value = vm.WorldState.GetTransientState(in storageCell); + stack.PushBytes(value); + + if (vm.TxTracer.IsTracingStorage) + { + if (gasAvailable < 0) return EvmExceptionType.OutOfGas; + vm.TxTracer.LoadOperationTransientStorage(storageCell.Address, result, value); + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionTStore(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + Metrics.TstoreOpcode++; + EvmState vmState = vm.State; + + if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; + + gasAvailable -= GasCostOf.TStore; + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + StorageCell storageCell = new(vmState.Env.ExecutingAccount, result); + Span bytes = stack.PopWord256(); + vm.WorldState.SetTransientState(in storageCell, !bytes.IsZero() ? bytes.ToArray() : BytesZero32); + if (vm.TxTracer.IsTracingStorage) + { + if (gasAvailable < 0) return EvmExceptionType.OutOfGas; + ReadOnlySpan currentValue = vm.WorldState.GetTransientState(in storageCell); + vm.TxTracer.SetOperationTransientStorage(storageCell.Address, result, bytes, currentValue); + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + Metrics.MCopyOpcode++; + + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 c)) return EvmExceptionType.StackUnderflow; + + gasAvailable -= GasCostOf.VeryLow + GasCostOf.VeryLow * EvmPooledMemory.Div32Ceiling(c); + EvmState vmState = vm.State; + if (!UpdateMemoryCost(vmState, ref gasAvailable, UInt256.Max(b, a), c)) return EvmExceptionType.OutOfGas; + + Span bytes = vmState.Memory.LoadSpan(in b, c); + + ITxTracer tracer = !vm.TxTracer.IsTracingInstructions ? null : vm.TxTracer; + + tracer?.ReportMemoryChange(b, bytes); + vmState.Memory.Save(in a, bytes); + tracer?.ReportMemoryChange(a, bytes); + + return EvmExceptionType.None; + } + + + [SkipLocalsInit] + [MethodImpl(MethodImplOptions.NoInlining)] + internal static EvmExceptionType InstructionSStore(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + Metrics.IncrementSStoreOpcode(); + EvmState vmState = vm.State; + if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; + IReleaseSpec spec = vm.Spec; + // fail fast before the first storage read if gas is not enough even for reset + if (!spec.UseNetGasMetering && !UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; + + if (spec.UseNetGasMeteringWithAStipendFix) + { + if (vm.TxTracer.IsTracingRefunds) + vm.TxTracer.ReportExtraGasPressure(GasCostOf.CallStipend - spec.GetNetMeteredSStoreCost() + 1); + if (gasAvailable <= GasCostOf.CallStipend) return EvmExceptionType.OutOfGas; + } + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + ReadOnlySpan bytes = stack.PopWord256(); + bool newIsZero = bytes.IsZero(); + bytes = !newIsZero ? bytes.WithoutLeadingZeros() : BytesZero; + + StorageCell storageCell = new(vmState.Env.ExecutingAccount, result); + + if (!ChargeStorageAccessGas( + ref gasAvailable, + vm, + in storageCell, + StorageAccessType.SSTORE, + spec)) return EvmExceptionType.OutOfGas; + + ReadOnlySpan currentValue = vm.WorldState.Get(in storageCell); + // Console.WriteLine($"current: {currentValue.ToHexString()} newValue {newValue.ToHexString()}"); + bool currentIsZero = currentValue.IsZero(); + + bool newSameAsCurrent = (newIsZero && currentIsZero) || Bytes.AreEqual(currentValue, bytes); + long sClearRefunds = RefundOf.SClear(spec.IsEip3529Enabled); + + if (!spec.UseNetGasMetering) // note that for this case we already deducted 5000 + { + if (newIsZero) + { + if (!newSameAsCurrent) + { + vmState.Refund += sClearRefunds; + if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(sClearRefunds); + } + } + else if (currentIsZero) + { + if (!UpdateGas(GasCostOf.SSet - GasCostOf.SReset, ref gasAvailable)) return EvmExceptionType.OutOfGas; + } + } + else // net metered + { + if (newSameAsCurrent) + { + if (!UpdateGas(spec.GetNetMeteredSStoreCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; + } + else // net metered, C != N + { + Span originalValue = vm.WorldState.GetOriginal(in storageCell); + bool originalIsZero = originalValue.IsZero(); + + bool currentSameAsOriginal = Bytes.AreEqual(originalValue, currentValue); + if (currentSameAsOriginal) + { + if (currentIsZero) + { + if (!UpdateGas(GasCostOf.SSet, ref gasAvailable)) return EvmExceptionType.OutOfGas; + } + else // net metered, current == original != new, !currentIsZero + { + if (!UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; + + if (newIsZero) + { + vmState.Refund += sClearRefunds; + if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(sClearRefunds); + } + } + } + else // net metered, new != current != original + { + long netMeteredStoreCost = spec.GetNetMeteredSStoreCost(); + if (!UpdateGas(netMeteredStoreCost, ref gasAvailable)) return EvmExceptionType.OutOfGas; + + if (!originalIsZero) // net metered, new != current != original != 0 + { + if (currentIsZero) + { + vmState.Refund -= sClearRefunds; + if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(-sClearRefunds); + } + + if (newIsZero) + { + vmState.Refund += sClearRefunds; + if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(sClearRefunds); + } + } + + bool newSameAsOriginal = Bytes.AreEqual(originalValue, bytes); + if (newSameAsOriginal) + { + long refundFromReversal; + if (originalIsZero) + { + refundFromReversal = spec.GetSetReversalRefund(); + } + else + { + refundFromReversal = spec.GetClearReversalRefund(); + } + + vmState.Refund += refundFromReversal; + if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(refundFromReversal); + } + } + } + } + + if (!newSameAsCurrent) + { + vm.WorldState.Set(in storageCell, newIsZero ? BytesZero : bytes.ToArray()); + } + + if (vm.TxTracer.IsTracingInstructions) + { + ReadOnlySpan valueToStore = newIsZero ? BytesZero.AsSpan() : bytes; + byte[] storageBytes = new byte[32]; // do not stackalloc here + storageCell.Index.ToBigEndian(storageBytes); + vm.TxTracer.ReportStorageChange(storageBytes, valueToStore); + } + + if (vm.TxTracer.IsTracingStorage) + { + vm.TxTracer.SetOperationStorage(storageCell.Address, result, bytes, currentValue); + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + [MethodImpl(MethodImplOptions.NoInlining)] + internal static EvmExceptionType InstructionSLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + IReleaseSpec spec = vm.Spec; + Metrics.IncrementSLoadOpcode(); + gasAvailable -= spec.GetSLoadCost(); + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + StorageCell storageCell = new(vm.State.Env.ExecutingAccount, result); + if (!ChargeStorageAccessGas( + ref gasAvailable, + vm, + in storageCell, + StorageAccessType.SLOAD, + spec)) return EvmExceptionType.OutOfGas; + + ReadOnlySpan value = vm.WorldState.Get(in storageCell); + stack.PushBytes(value); + if (vm.TxTracer.IsTracingStorage) + { + vm.TxTracer.LoadOperationStorage(storageCell.Address, result, value); + } + + return EvmExceptionType.None; + } + + internal static bool ChargeStorageAccessGas( + ref long gasAvailable, + IEvm vm, + in StorageCell storageCell, + StorageAccessType storageAccessType, + IReleaseSpec spec) + { + EvmState vmState = vm.State; + bool result = true; + if (spec.UseHotAndColdStorage) + { + if (vm.TxTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list + { + vmState.AccessTracker.WarmUp(in storageCell); + } + + if (vmState.AccessTracker.IsCold(in storageCell)) + { + result = UpdateGas(GasCostOf.ColdSLoad, ref gasAvailable); + vmState.AccessTracker.WarmUp(in storageCell); + } + else if (storageAccessType == StorageAccessType.SLOAD) + { + // we do not charge for WARM_STORAGE_READ_COST in SSTORE scenario + result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); + } + } + + return result; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs new file mode 100644 index 00000000000..f8d8c685aae --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -0,0 +1,525 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using static Nethermind.Evm.VirtualMachine; + +namespace Nethermind.Evm; +using unsafe OpCode = delegate*; +using Int256; + +internal unsafe sealed partial class EvmInstructions +{ + public static OpCode[] GenerateOpCodes(IReleaseSpec spec) + { + var lookup = new delegate*[256]; + + for (int i = 0; i < lookup.Length; i++) + { + lookup[i] = &InstructionBadInstruction; + } + + lookup[(int)Instruction.STOP] = &InstructionStop; + lookup[(int)Instruction.ADD] = &InstructionMath2Param; + lookup[(int)Instruction.MUL] = &InstructionMath2Param; + lookup[(int)Instruction.SUB] = &InstructionMath2Param; + lookup[(int)Instruction.DIV] = &InstructionMath2Param; + lookup[(int)Instruction.SDIV] = &InstructionMath2Param; + lookup[(int)Instruction.MOD] = &InstructionMath2Param; + lookup[(int)Instruction.SMOD] = &InstructionMath2Param; + lookup[(int)Instruction.ADDMOD] = &InstructionMath3Param; + lookup[(int)Instruction.MULMOD] = &InstructionMath3Param; + lookup[(int)Instruction.EXP] = &InstructionExp; + lookup[(int)Instruction.SIGNEXTEND] = &InstructionSignExtend; + lookup[(int)Instruction.LT] = &InstructionMath2Param; + lookup[(int)Instruction.GT] = &InstructionMath2Param; + lookup[(int)Instruction.SLT] = &InstructionMath2Param; + lookup[(int)Instruction.SGT] = &InstructionMath2Param; + lookup[(int)Instruction.EQ] = &InstructionBitwise; + lookup[(int)Instruction.ISZERO] = &InstructionMath1Param; + lookup[(int)Instruction.AND] = &InstructionBitwise; + lookup[(int)Instruction.OR] = &InstructionBitwise; + lookup[(int)Instruction.XOR] = &InstructionBitwise; + lookup[(int)Instruction.NOT] = &InstructionMath1Param; + lookup[(int)Instruction.BYTE] = &InstructionByte; + if (spec.ShiftOpcodesEnabled) + { + lookup[(int)Instruction.SHL] = &InstructionShift; + lookup[(int)Instruction.SHR] = &InstructionShift; + lookup[(int)Instruction.SAR] = &InstructionSar; + } + + lookup[(int)Instruction.KECCAK256] = &InstructionKeccak256; + + lookup[(int)Instruction.ADDRESS] = &InstructionEnvBytes; + lookup[(int)Instruction.BALANCE] = &InstructionBalance; + lookup[(int)Instruction.ORIGIN] = &InstructionEnvBytes; + lookup[(int)Instruction.CALLER] = &InstructionEnvBytes; + lookup[(int)Instruction.CALLVALUE] = &InstructionEnvUInt256; + lookup[(int)Instruction.CALLDATALOAD] = &InstructionCallDataLoad; + lookup[(int)Instruction.CALLDATASIZE] = &InstructionEnvUInt256; + lookup[(int)Instruction.CALLDATACOPY] = &InstructionCodeCopy; + lookup[(int)Instruction.CODESIZE] = &InstructionEnvUInt256; + lookup[(int)Instruction.CODECOPY] = &InstructionCodeCopy; + lookup[(int)Instruction.GASPRICE] = &InstructionEnvUInt256; + + lookup[(int)Instruction.EXTCODESIZE] = &InstructionExtCodeSize; + + lookup[(int)Instruction.EXTCODECOPY] = &InstructionExtCodeCopy; + + if (spec.ReturnDataOpcodesEnabled) + { + lookup[(int)Instruction.RETURNDATASIZE] = &InstructionReturnDataSize; + lookup[(int)Instruction.RETURNDATACOPY] = &InstructionReturnDataCopy; + } + if (spec.ExtCodeHashOpcodeEnabled) + { + lookup[(int)Instruction.EXTCODEHASH] = &InstructionExtCodeHash; + } + + lookup[(int)Instruction.BLOCKHASH] = &InstructionBlockHash; + + lookup[(int)Instruction.COINBASE] = &InstructionEnvBytes; + lookup[(int)Instruction.TIMESTAMP] = &InstructionEnvUInt256; + lookup[(int)Instruction.NUMBER] = &InstructionEnvUInt256; + lookup[(int)Instruction.PREVRANDAO] = &InstructionPrevRandao; + lookup[(int)Instruction.GASLIMIT] = &InstructionEnvUInt256; + lookup[(int)Instruction.CHAINID] = &InstructionChainId; + + if (spec.SelfBalanceOpcodeEnabled) + { + lookup[(int)Instruction.SELFBALANCE] = &InstructionSelfBalance; + } + + lookup[(int)Instruction.BASEFEE] = &InstructionEnvUInt256; + lookup[(int)Instruction.BLOBHASH] = &InstructionBlobHash; + lookup[(int)Instruction.BLOBBASEFEE] = &InstructionEnvUInt256; + // Gap: 0x4b to 0x4f + lookup[(int)Instruction.POP] = &InstructionPop; + lookup[(int)Instruction.MLOAD] = &InstructionMLoad; + lookup[(int)Instruction.MSTORE] = &InstructionMStore; + lookup[(int)Instruction.MSTORE8] = &InstructionMStore8; + lookup[(int)Instruction.SLOAD] = &InstructionSLoad; + lookup[(int)Instruction.SSTORE] = &InstructionSStore; + lookup[(int)Instruction.JUMP] = &InstructionJump; + lookup[(int)Instruction.JUMPI] = &InstructionJumpIf; + lookup[(int)Instruction.PC] = &InstructionProgramCounter; + lookup[(int)Instruction.MSIZE] = &InstructionEnvUInt256; + lookup[(int)Instruction.GAS] = &InstructionGas; + lookup[(int)Instruction.JUMPDEST] = &InstructionJumpDest; + + if (spec.TransientStorageEnabled) + { + lookup[(int)Instruction.TLOAD] = &InstructionTLoad; + lookup[(int)Instruction.TSTORE] = &InstructionTStore; + } + if (spec.MCopyIncluded) + { + lookup[(int)Instruction.MCOPY] = &InstructionMCopy; + } + + if (spec.IncludePush0Instruction) + { + lookup[(int)Instruction.PUSH0] = &InstructionPush0; + } + + lookup[(int)Instruction.PUSH1] = &InstructionPush; + lookup[(int)Instruction.PUSH2] = &InstructionPush; + lookup[(int)Instruction.PUSH3] = &InstructionPush; + lookup[(int)Instruction.PUSH4] = &InstructionPush; + lookup[(int)Instruction.PUSH5] = &InstructionPush; + lookup[(int)Instruction.PUSH6] = &InstructionPush; + lookup[(int)Instruction.PUSH7] = &InstructionPush; + lookup[(int)Instruction.PUSH8] = &InstructionPush; + lookup[(int)Instruction.PUSH9] = &InstructionPush; + lookup[(int)Instruction.PUSH10] = &InstructionPush; + lookup[(int)Instruction.PUSH11] = &InstructionPush; + lookup[(int)Instruction.PUSH12] = &InstructionPush; + lookup[(int)Instruction.PUSH13] = &InstructionPush; + lookup[(int)Instruction.PUSH14] = &InstructionPush; + lookup[(int)Instruction.PUSH15] = &InstructionPush; + lookup[(int)Instruction.PUSH16] = &InstructionPush; + lookup[(int)Instruction.PUSH17] = &InstructionPush; + lookup[(int)Instruction.PUSH18] = &InstructionPush; + lookup[(int)Instruction.PUSH19] = &InstructionPush; + lookup[(int)Instruction.PUSH20] = &InstructionPush; + lookup[(int)Instruction.PUSH21] = &InstructionPush; + lookup[(int)Instruction.PUSH22] = &InstructionPush; + lookup[(int)Instruction.PUSH23] = &InstructionPush; + lookup[(int)Instruction.PUSH24] = &InstructionPush; + lookup[(int)Instruction.PUSH25] = &InstructionPush; + lookup[(int)Instruction.PUSH26] = &InstructionPush; + lookup[(int)Instruction.PUSH27] = &InstructionPush; + lookup[(int)Instruction.PUSH28] = &InstructionPush; + lookup[(int)Instruction.PUSH29] = &InstructionPush; + lookup[(int)Instruction.PUSH30] = &InstructionPush; + lookup[(int)Instruction.PUSH31] = &InstructionPush; + lookup[(int)Instruction.PUSH32] = &InstructionPush; + + lookup[(int)Instruction.DUP1] = &InstructionDup; + lookup[(int)Instruction.DUP2] = &InstructionDup; + lookup[(int)Instruction.DUP3] = &InstructionDup; + lookup[(int)Instruction.DUP4] = &InstructionDup; + lookup[(int)Instruction.DUP5] = &InstructionDup; + lookup[(int)Instruction.DUP6] = &InstructionDup; + lookup[(int)Instruction.DUP7] = &InstructionDup; + lookup[(int)Instruction.DUP8] = &InstructionDup; + lookup[(int)Instruction.DUP9] = &InstructionDup; + lookup[(int)Instruction.DUP10] = &InstructionDup; + lookup[(int)Instruction.DUP11] = &InstructionDup; + lookup[(int)Instruction.DUP12] = &InstructionDup; + lookup[(int)Instruction.DUP13] = &InstructionDup; + lookup[(int)Instruction.DUP14] = &InstructionDup; + lookup[(int)Instruction.DUP15] = &InstructionDup; + lookup[(int)Instruction.DUP16] = &InstructionDup; + + lookup[(int)Instruction.SWAP1] = &InstructionSwap; + lookup[(int)Instruction.SWAP2] = &InstructionSwap; + lookup[(int)Instruction.SWAP3] = &InstructionSwap; + lookup[(int)Instruction.SWAP4] = &InstructionSwap; + lookup[(int)Instruction.SWAP5] = &InstructionSwap; + lookup[(int)Instruction.SWAP6] = &InstructionSwap; + lookup[(int)Instruction.SWAP7] = &InstructionSwap; + lookup[(int)Instruction.SWAP8] = &InstructionSwap; + lookup[(int)Instruction.SWAP9] = &InstructionSwap; + lookup[(int)Instruction.SWAP10] = &InstructionSwap; + lookup[(int)Instruction.SWAP11] = &InstructionSwap; + lookup[(int)Instruction.SWAP12] = &InstructionSwap; + lookup[(int)Instruction.SWAP13] = &InstructionSwap; + lookup[(int)Instruction.SWAP14] = &InstructionSwap; + lookup[(int)Instruction.SWAP15] = &InstructionSwap; + lookup[(int)Instruction.SWAP16] = &InstructionSwap; + + lookup[(int)Instruction.LOG0] = &InstructionLog; + lookup[(int)Instruction.LOG1] = &InstructionLog; + lookup[(int)Instruction.LOG2] = &InstructionLog; + lookup[(int)Instruction.LOG3] = &InstructionLog; + lookup[(int)Instruction.LOG4] = &InstructionLog; + + 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] = &InstructionDupN; + lookup[(int)Instruction.SWAPN] = &InstructionSwapN; + lookup[(int)Instruction.EXCHANGE] = &InstructionExchange; + lookup[(int)Instruction.EOFCREATE] = &InstructionEofCreate; + lookup[(int)Instruction.RETURNCONTRACT] = &InstructionReturnContract; + } + + lookup[(int)Instruction.CREATE] = &InstructionCreate; + lookup[(int)Instruction.CALL] = &InstructionCall; + lookup[(int)Instruction.CALLCODE] = &InstructionCall; + lookup[(int)Instruction.RETURN] = &InstructionReturn; + if (spec.DelegateCallEnabled) + { + lookup[(int)Instruction.DELEGATECALL] = &InstructionCall; + } + if (spec.Create2OpcodeEnabled) + { + lookup[(int)Instruction.CREATE2] = &InstructionCreate; + } + + lookup[(int)Instruction.RETURNDATALOAD] = &InstructionReturnDataLoad; + if (spec.StaticCallEnabled) + { + lookup[(int)Instruction.STATICCALL] = &InstructionCall; + } + + if (spec.IsEofEnabled) + { + lookup[(int)Instruction.EXTCALL] = &InstructionEofCall; + if (spec.DelegateCallEnabled) + { + lookup[(int)Instruction.EXTDELEGATECALL] = &InstructionEofCall; + } + if (spec.StaticCallEnabled) + { + lookup[(int)Instruction.EXTSTATICCALL] = &InstructionEofCall; + } + } + + if (spec.RevertOpcodeEnabled) + { + lookup[(int)Instruction.REVERT] = &InstructionRevert; + } + + lookup[(int)Instruction.INVALID] = &InstructionInvalid; + lookup[(int)Instruction.SELFDESTRUCT] = &InstructionSelfDestruct; + + return lookup; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionStop(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + if (vm.State.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) + { + return EvmExceptionType.BadInstruction; + } + + return EvmExceptionType.Stop; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionRevert(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + if (!stack.PopUInt256(out UInt256 position) || + !stack.PopUInt256(out UInt256 length)) + return EvmExceptionType.StackUnderflow; + + if (!UpdateMemoryCost(vm.State, ref gasAvailable, in position, in length)) + { + return EvmExceptionType.OutOfGas; + } + + vm.ReturnData = vm.State.Memory.Load(in position, in length).ToArray(); + + return EvmExceptionType.Revert; + } + + [SkipLocalsInit] + private static EvmExceptionType InstructionSelfDestruct(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + Metrics.IncrementSelfDestructs(); + + EvmState vmState = vm.State; + var spec = vm.Spec; + var state = vm.WorldState; + + if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; + + if (spec.UseShanghaiDDosProtection) + { + gasAvailable -= GasCostOf.SelfDestructEip150; + } + + Address inheritor = stack.PopAddress(); + if (inheritor is null) return EvmExceptionType.StackUnderflow; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, inheritor, false)) return EvmExceptionType.OutOfGas; + + Address executingAccount = vmState.Env.ExecutingAccount; + bool createInSameTx = vmState.AccessTracker.CreateList.Contains(executingAccount); + if (!spec.SelfdestructOnlyOnSameTransaction || createInSameTx) + vmState.AccessTracker.ToBeDestroyed(executingAccount); + + UInt256 result = state.GetBalance(executingAccount); + if (vm.TxTracer.IsTracingActions) vm.TxTracer.ReportSelfDestruct(executingAccount, result, inheritor); + if (spec.ClearEmptyAccountWhenTouched && !result.IsZero && state.IsDeadAccount(inheritor)) + { + if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; + } + + bool inheritorAccountExists = state.AccountExists(inheritor); + if (!spec.ClearEmptyAccountWhenTouched && !inheritorAccountExists && spec.UseShanghaiDDosProtection) + { + if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; + } + + if (!inheritorAccountExists) + { + state.CreateAccount(inheritor, result); + } + else if (!inheritor.Equals(executingAccount)) + { + state.AddToBalance(inheritor, result, spec); + } + + if (spec.SelfdestructOnlyOnSameTransaction && !createInSameTx && inheritor.Equals(executingAccount)) + return EvmExceptionType.Stop; // don't burn eth when contract is not destroyed per EIP clarification + + state.SubtractFromBalance(executingAccount, result, spec); + return EvmExceptionType.Stop; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionPrevRandao(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Base; + BlockHeader header = vm.State.Env.TxExecutionContext.BlockExecutionContext.Header; + if (header.IsPostMerge) + { + stack.PushBytes(header.Random.Bytes); + } + else + { + UInt256 result = header.Difficulty; + stack.PushUInt256(in result); + } + + return EvmExceptionType.None; + } + + public static EvmExceptionType InstructionInvalid(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.High; + return EvmExceptionType.BadInstruction; + } + + public static EvmExceptionType InstructionBadInstruction(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + => EvmExceptionType.BadInstruction; + + [SkipLocalsInit] + public static EvmExceptionType InstructionExp(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Exp; + + if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + Span bytes = stack.PopWord256(); + + int leadingZeros = bytes.LeadingZerosCount(); + if (leadingZeros == 32) + { + stack.PushOne(); + } + else + { + int expSize = 32 - leadingZeros; + gasAvailable -= vm.Spec.GetExpByteCost() * expSize; + + if (a.IsZero) + { + stack.PushZero(); + } + else if (a.IsOne) + { + stack.PushOne(); + } + else + { + UInt256.Exp(a, new UInt256(bytes, true), out UInt256 result); + stack.PushUInt256(in result); + } + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionByte(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + Span bytes = stack.PopWord256(); + + if (a >= BigInt32) + { + stack.PushZero(); + } + else + { + int adjustedPosition = bytes.Length - 32 + (int)a; + if (adjustedPosition < 0) + { + stack.PushZero(); + } + else + { + stack.PushByte(bytes[adjustedPosition]); + } + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionSignExtend(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Low; + + if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + if (a >= BigInt32) + { + if (!stack.EnsureDepth(1)) return EvmExceptionType.StackUnderflow; + return EvmExceptionType.None; + } + + int position = 31 - (int)a; + + Span bytes = stack.PeekWord256(); + sbyte sign = (sbyte)bytes[position]; + + if (sign >= 0) + { + BytesZero32.AsSpan(0, position).CopyTo(bytes[..position]); + } + else + { + BytesMax32.AsSpan(0, position).CopyTo(bytes[..position]); + } + + // Didn't remove from stack so don't need to push back + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionKeccak256(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + gasAvailable -= GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in b); + + EvmState vmState = vm.State; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; + + Span bytes = vmState.Memory.LoadSpan(in a, b); + stack.PushBytes(ValueKeccak.Compute(bytes).BytesAsSpan); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionCallDataLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + stack.PushBytes(vm.State.Env.InputData.SliceWithZeroPadding(result, 32)); + + return EvmExceptionType.None; + } + + public static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) + { + long memoryCost = vmState.Memory.CalculateMemoryCost(in position, length); + if (memoryCost != 0L) + { + if (!UpdateGas(memoryCost, ref gasAvailable)) + { + return false; + } + } + + return true; + } + + public static bool UpdateGas(long gasCost, ref long gasAvailable) + { + if (gasAvailable < gasCost) + { + return false; + } + + gasAvailable -= gasCost; + return true; + } + + public static void UpdateGasUp(long refund, ref long gasAvailable) + { + gasAvailable += refund; + } +} diff --git a/src/Nethermind/Nethermind.Evm/StackPool.cs b/src/Nethermind/Nethermind.Evm/StackPool.cs index 07cc466777f..7d2b9855612 100644 --- a/src/Nethermind/Nethermind.Evm/StackPool.cs +++ b/src/Nethermind/Nethermind.Evm/StackPool.cs @@ -50,28 +50,3 @@ public void ReturnStacks(byte[] dataStack, ReturnState[] returnStack) } } -public static class 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; - - [StackTraceHidden] - [DoesNotReturn] - internal static void ThrowEvmStackUnderflowException() - { - Metrics.EvmExceptions++; - throw new EvmStackUnderflowException(); - } - - [StackTraceHidden] - [DoesNotReturn] - internal static void ThrowEvmStackOverflowException() - { - Metrics.EvmExceptions++; - throw new EvmStackOverflowException(); - } -} - diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 0674fdc6132..4ef56ee258c 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -641,9 +641,7 @@ protected virtual void ExecuteEvmCall( using (EvmState state = EvmState.RentTopLevel(unspentGas, executionType, snapshot, env, accessedItems)) { - substate = !tracer.IsTracingActions - ? VirtualMachine.Run(state, WorldState, tracer) - : VirtualMachine.Run(state, WorldState, tracer); + substate = VirtualMachine.Run(state, WorldState, tracer); unspentGas = state.GasAvailable; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index eb25a7a75b8..2975f192113 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -20,6 +20,7 @@ using Nethermind.State; using static Nethermind.Evm.VirtualMachine; using static System.Runtime.CompilerServices.Unsafe; +using static Nethermind.Evm.EvmInstructions; using static Nethermind.Evm.EvmObjectFormat.EofValidator; #if DEBUG @@ -29,13 +30,15 @@ [assembly: InternalsVisibleTo("Nethermind.Evm.Test")] namespace Nethermind.Evm; + +using unsafe OpCode = delegate*; using Int256; using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.EvmObjectFormat.Handlers; +using System.Runtime.InteropServices; - -public class VirtualMachine : IVirtualMachine +public unsafe class VirtualMachine : IEvm, IVirtualMachine { public const int MaxCallDepth = Eof1.RETURN_STACK_MAX_HEIGHT; private readonly static UInt256 P255Int = (UInt256)System.Numerics.BigInteger.Pow(2, 255); @@ -65,7 +68,7 @@ public class VirtualMachine : IVirtualMachine internal static readonly PrecompileExecutionFailureException PrecompileExecutionFailureException = new(); internal static readonly OutOfGasException PrecompileOutOfGasException = new(); - private readonly IVirtualMachine _evm; + //private readonly IVirtualMachine _evm; public VirtualMachine( IBlockhashProvider? blockhashProvider, @@ -73,16 +76,13 @@ public VirtualMachine( ICodeInfoRepository codeInfoRepository, ILogManager? logManager) { - ILogger logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - _evm = logger.IsTrace - ? new VirtualMachine(blockhashProvider, specProvider, logger) - : new VirtualMachine(blockhashProvider, specProvider, logger); + _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); + _blockhashProvider = blockhashProvider ?? throw new ArgumentNullException(nameof(blockhashProvider)); + _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); + _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); + _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); } - public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) - where TTracingActions : struct, VirtualMachine.IIsTracing - => _evm.Run(state, worldState, txTracer); - internal readonly ref struct CallResult { public static CallResult InvalidSubroutineEntry => new(EvmExceptionType.InvalidSubroutineEntry); @@ -96,8 +96,7 @@ internal readonly ref struct CallResult public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); public static CallResult InvalidAddressRange => new(EvmExceptionType.AddressOutOfRange); - public static object BoxedEmpty { get; } = new object(); - public static CallResult Empty(int fromVersion) => new(default, null, fromVersion); + public static CallResult Empty(int fromVersion) => new(null, default, null, fromVersion); public CallResult(EvmState stateToExecute) { @@ -118,7 +117,7 @@ public CallResult(ReadOnlyMemory output, bool? precompileSuccess, int from FromVersion = fromVersion; } - public CallResult(ICodeInfo container, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + public CallResult(ICodeInfo? container, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) { StateToExecute = null; Output = (container, output); @@ -151,10 +150,7 @@ private CallResult(EvmExceptionType exceptionType) public interface IIsTracing { } public readonly struct NotTracing : IIsTracing { } public readonly struct IsTracing : IIsTracing { } -} -internal sealed class VirtualMachine : IVirtualMachine where TLogger : struct, IIsTracing -{ private readonly byte[] _chainId; private readonly IBlockhashProvider _blockhashProvider; @@ -165,24 +161,41 @@ internal sealed class VirtualMachine : IVirtualMachine where TLogger : private (Address Address, bool ShouldDelete) _parityTouchBugAccount = (Address.FromNumber(3), false); private ReadOnlyMemory _returnDataBuffer = Array.Empty(); private ITxTracer _txTracer = NullTxTracer.Instance; + private readonly ICodeInfoRepository _codeInfoRepository; + private IReleaseSpec _spec; + + public ICodeInfoRepository CodeInfoRepository => _codeInfoRepository; + public IReleaseSpec Spec => _spec; + public ITxTracer TxTracer => _txTracer; + public IWorldState WorldState => _state; + public ReadOnlySpan ChainId => _chainId; + ReadOnlyMemory IEvm.ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } + object IEvm.ReturnData { get => _returnData; set => _returnData = value; } + object _returnData; + IBlockhashProvider IEvm.BlockhashProvider => _blockhashProvider; + + OpCode[] _opcodeMethods; public VirtualMachine( IBlockhashProvider? blockhashProvider, ISpecProvider? specProvider, + ICodeInfoRepository codeInfoRepository, ILogger? logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _blockhashProvider = blockhashProvider ?? throw new ArgumentNullException(nameof(blockhashProvider)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); + _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); } - public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) - where TTracingActions : struct, IIsTracing + public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) { _txTracer = txTracer; _state = worldState; + _spec = _specProvider.GetSpec(state.Env.TxExecutionContext.BlockExecutionContext.Header.Number, state.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp); + _opcodeMethods = (OpCode[])(_spec.EvmInstructions ??= EvmInstructions.GenerateOpCodes(_spec)); ref readonly TxExecutionContext txExecutionContext = ref state.Env.TxExecutionContext; ICodeInfoRepository codeInfoRepository = txExecutionContext.CodeInfoRepository; IReleaseSpec spec = _specProvider.GetSpec(txExecutionContext.BlockExecutionContext.Header.Number, txExecutionContext.BlockExecutionContext.Header.Timestamp); @@ -205,12 +218,12 @@ public TransactionSubstate Run(EvmState state, IWorldState worl CallResult callResult; if (currentState.IsPrecompile) { - if (typeof(TTracingActions) == typeof(IsTracing)) + if (_txTracer.IsTracingActions) { _txTracer.ReportAction(currentState.GasAvailable, currentState.Env.Value, currentState.From, currentState.To, currentState.Env.InputData, currentState.ExecutionType, true); } - callResult = ExecutePrecompile(currentState, spec); + callResult = ExecutePrecompile(currentState); if (!callResult.PrecompileSuccess.Value) { @@ -232,7 +245,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } else { - if (typeof(TTracingActions) == typeof(IsTracing) && !currentState.IsContinuation) + if (_txTracer.IsTracingActions && !currentState.IsContinuation) { _txTracer.ReportAction(currentState.GasAvailable, currentState.Env.Value, @@ -246,9 +259,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (_txTracer.IsTracingCode) _txTracer.ReportByteCode(currentState.Env.CodeInfo.MachineCode); } - callResult = !_txTracer.IsTracingInstructions - ? ExecuteCall(currentState, previousCallResult, previousCallOutput, previousCallOutputDestination, spec) - : ExecuteCall(currentState, previousCallResult, previousCallOutput, previousCallOutputDestination, spec); + callResult = ExecuteCall(currentState, previousCallResult, previousCallOutput, previousCallOutputDestination); if (!callResult.IsReturn) { @@ -262,10 +273,10 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (callResult.IsException) { - if (typeof(TTracingActions) == typeof(IsTracing)) _txTracer.ReportActionError(callResult.ExceptionType); + if (_txTracer.IsTracingActions) _txTracer.ReportActionError(callResult.ExceptionType); _state.Restore(currentState.Snapshot); - RevertParityTouchBugAccount(spec); + RevertParityTouchBugAccount(); if (currentState.IsTopLevel) { @@ -286,9 +297,9 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (currentState.IsTopLevel) { - if (typeof(TTracingActions) == typeof(IsTracing)) + if (_txTracer.IsTracingActions) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(_spec, callResult.Output.Bytes.Length); if (callResult.IsException) { @@ -481,7 +492,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } } - if (typeof(TTracingActions) == typeof(IsTracing)) + if (_txTracer.IsTracingActions) { _txTracer.ReportActionEnd(previousState.GasAvailable, _returnDataBuffer); } @@ -503,7 +514,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl previousCallOutputDestination = (ulong)previousState.OutputDestination; - if (typeof(TTracingActions) == typeof(IsTracing)) + if (_txTracer.IsTracingActions) { _txTracer.ReportActionRevert(previousState.GasAvailable, callResult.Output.Bytes); } @@ -520,11 +531,11 @@ public TransactionSubstate Run(EvmState state, IWorldState worl Failure: { - if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"exception ({failure.GetType().Name}) in {currentState.ExecutionType} at depth {currentState.Env.CallDepth} - restoring snapshot"); + if (_logger.IsTrace) _logger.Trace($"exception ({failure.GetType().Name}) in {currentState.ExecutionType} at depth {currentState.Env.CallDepth} - restoring snapshot"); _state.Restore(currentState.Snapshot); - RevertParityTouchBugAccount(spec); + RevertParityTouchBugAccount(); if (txTracer.IsTracingInstructions) { @@ -532,7 +543,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl txTracer.ReportOperationError(failure is EvmException evmException ? evmException.ExceptionType : EvmExceptionType.Other); } - if (typeof(TTracingActions) == typeof(IsTracing)) + if (_txTracer.IsTracingActions) { EvmException evmException = failure as EvmException; _txTracer.ReportActionError(evmException?.ExceptionType ?? EvmExceptionType.Other); @@ -555,13 +566,13 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } } - private void RevertParityTouchBugAccount(IReleaseSpec spec) + private void RevertParityTouchBugAccount() { if (_parityTouchBugAccount.ShouldDelete) { if (_state.AccountExists(_parityTouchBugAccount.Address)) { - _state.AddToBalance(_parityTouchBugAccount.Address, UInt256.Zero, spec); + _state.AddToBalance(_parityTouchBugAccount.Address, UInt256.Zero, _spec); } _parityTouchBugAccount.ShouldDelete = false; @@ -655,17 +666,17 @@ private bool ChargeStorageAccessGas( return result; } - private CallResult ExecutePrecompile(EvmState state, IReleaseSpec spec) + private CallResult ExecutePrecompile(EvmState state) { ReadOnlyMemory callData = state.Env.InputData; UInt256 transferValue = state.Env.TransferValue; long gasAvailable = state.GasAvailable; IPrecompile precompile = state.Env.CodeInfo.Precompile; - long baseGasCost = precompile.BaseGasCost(spec); - long blobGasCost = precompile.DataGasCost(callData, spec); + long baseGasCost = precompile.BaseGasCost(_spec); + long blobGasCost = precompile.DataGasCost(callData, _spec); - bool wasCreated = _state.AddToBalanceAndCreateIfNotExists(state.Env.ExecutingAccount, transferValue, spec); + bool wasCreated = _state.AddToBalanceAndCreateIfNotExists(state.Env.ExecutingAccount, transferValue, _spec); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md // An additional issue was found in Parity, @@ -678,7 +689,7 @@ private CallResult ExecutePrecompile(EvmState state, IReleaseSpec spec) if (state.Env.ExecutingAccount.Equals(_parityTouchBugAccount.Address) && !wasCreated && transferValue.IsZero - && spec.ClearEmptyAccountWhenTouched) + && _spec.ClearEmptyAccountWhenTouched) { _parityTouchBugAccount.ShouldDelete = true; } @@ -692,7 +703,7 @@ private CallResult ExecutePrecompile(EvmState state, IReleaseSpec spec) try { - (ReadOnlyMemory output, bool success) = precompile.Run(callData, spec); + (ReadOnlyMemory output, bool success) = precompile.Run(callData, _spec); CallResult callResult = new(output, success, 0, !success); return callResult; } @@ -715,15 +726,14 @@ private CallResult ExecutePrecompile(EvmState state, IReleaseSpec spec) /// values at compile time. /// [SkipLocalsInit] - private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousCallResult, ZeroPaddedSpan previousCallOutput, scoped in UInt256 previousCallOutputDestination, IReleaseSpec spec) - where TTracingInstructions : struct, IIsTracing + private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousCallResult, ZeroPaddedSpan previousCallOutput, scoped in UInt256 previousCallOutputDestination) { ref readonly ExecutionEnvironment env = ref vmState.Env; if (!vmState.IsContinuation) { - _state.AddToBalanceAndCreateIfNotExists(env.ExecutingAccount, env.TransferValue, spec); + _state.AddToBalanceAndCreateIfNotExists(env.ExecutingAccount, env.TransferValue, _spec); - if (vmState.ExecutionType.IsAnyCreate() && spec.ClearEmptyAccountWhenTouched) + if (vmState.ExecutionType.IsAnyCreate() && _spec.ClearEmptyAccountWhenTouched) { _state.IncrementNonce(env.ExecutingAccount); } @@ -739,13 +749,13 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM } vmState.InitializeStacks(); - EvmStack stack = new(vmState.DataStackHead, _txTracer, vmState.DataStack.AsSpan()); + EvmStack stack = new(vmState.DataStackHead, _txTracer, vmState.DataStack.AsSpan()); long gasAvailable = vmState.GasAvailable; if (previousCallResult is not null) { stack.PushBytes(previousCallResult.Value.Span); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationRemainingGas(vmState.GasAvailable); + if (_txTracer.IsTracingInstructions) _txTracer.ReportOperationRemainingGas(vmState.GasAvailable); } if (previousCallOutput.Length > 0) @@ -759,2724 +769,291 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM vmState.Memory.Save(in localPreviousDest, previousCallOutput); } + _vmState = vmState; // Struct generic parameter is used to burn out all the if statements // and inner code by typeof(TTracing) == typeof(NotTracing) // checks that are evaluated to constant values at compile time. // This only works for structs, not for classes or interface types // which use shared generics. - if (!_txTracer.IsTracingRefunds) - { - return _txTracer.IsTracingOpLevelStorage ? - ExecuteCode(vmState, ref stack, gasAvailable, spec) : - ExecuteCode(vmState, ref stack, gasAvailable, spec); - } - else - { - return _txTracer.IsTracingOpLevelStorage ? - ExecuteCode(vmState, ref stack, gasAvailable, spec) : - ExecuteCode(vmState, ref stack, gasAvailable, spec); - } + return ExecuteCode(ref stack, gasAvailable); Empty: return CallResult.Empty(vmState.Env.CodeInfo.Version); OutOfGas: return CallResult.OutOfGasException; } + EvmState IEvm.State => _vmState; + EvmState _vmState; + int IEvm.SectionIndex { get => _sectionIndex; set => _sectionIndex = value; } + int _sectionIndex; + [SkipLocalsInit] - private CallResult ExecuteCode(EvmState vmState, scoped ref EvmStack stack, long gasAvailable, IReleaseSpec spec) - where TTracingInstructions : struct, IIsTracing - where TTracingRefunds : struct, IIsTracing - where TTracingStorage : struct, IIsTracing + private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailable) { + _returnData = null; + _sectionIndex = _vmState.FunctionIndex; - ref readonly ExecutionEnvironment env = ref vmState.Env; - ref readonly TxExecutionContext txCtx = ref env.TxExecutionContext; - ref readonly BlockExecutionContext blkCtx = ref txCtx.BlockExecutionContext; + ICodeInfo codeInfo = _vmState.Env.CodeInfo; + ReadOnlySpan codeSection = GetInstructions(codeInfo); - int programCounter = vmState.ProgramCounter; - int sectionIndex = vmState.FunctionIndex; + EvmExceptionType exceptionType = EvmExceptionType.None; - ReadOnlySpan codeSection = env.CodeInfo.CodeSection.Span; - ReadOnlySpan dataSection = env.CodeInfo.DataSection.Span; + var tracingInstructions = _txTracer.IsTracingInstructions; + var isCancelable = _txTracer.IsCancelable; - EvmExceptionType exceptionType = EvmExceptionType.None; - bool isRevert = false; -#if DEBUG - DebugTracer? debugger = _txTracer.GetTracer(); -#endif - SkipInit(out UInt256 a); - SkipInit(out UInt256 b); - SkipInit(out UInt256 c); - SkipInit(out UInt256 result); - SkipInit(out StorageCell storageCell); - object returnData; - ZeroPaddedSpan slice; - bool isCancelable = _txTracer.IsCancelable; - uint codeLength = (uint)codeSection.Length; - while ((uint)programCounter < codeLength) + OpCode[] opcodeMethods = _opcodeMethods; + // Initialize program counter to the current state's value. + // Entry point is not always 0 as we may be returning to code after a call. + int programCounter = _vmState.ProgramCounter; + // We use a while loop rather than a for loop as some + // opcodes can change the program counter (e.g. Push, Jump, etc) + while ((uint)programCounter < (uint)codeSection.Length) { -#if DEBUG - debugger?.TryWait(ref vmState, ref programCounter, ref gasAvailable, ref stack.Head); -#endif - Instruction instruction = (Instruction)codeSection[programCounter]; + // Get the opcode at the current program counter + Instruction instruction = codeSection[programCounter]; - if (isCancelable && _txTracer.IsCancelled) - { - ThrowOperationCanceledException(); - } + if (isCancelable && _txTracer.IsCancelled) ThrowOperationCanceledException(); - // Evaluated to constant at compile time and code elided if not tracing - if (typeof(TTracingInstructions) == typeof(IsTracing)) - StartInstructionTrace(instruction, vmState, gasAvailable, programCounter, sectionIndex, in stack); + if (tracingInstructions) + StartInstructionTrace(instruction, gasAvailable, programCounter, in stack); + // Advance the program counter one instruction programCounter++; - Span bytes; - switch (instruction) - { - case Instruction.STOP: - { - if (vmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) - { - goto InvalidInstruction; - } - goto EmptyReturn; - } - case Instruction.ADD: - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out a)) goto StackUnderflow; - UInt256.Add(in a, in b, out result); - stack.PushUInt256(result); - - break; - } - case Instruction.MUL: - { - gasAvailable -= GasCostOf.Low; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - UInt256.Multiply(in a, in b, out result); - stack.PushUInt256(in result); - break; - } - case Instruction.SUB: - { - gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - UInt256.Subtract(in a, in b, out result); + // Get the opcode delegate* from the opcode array + OpCode opcodeMethod = opcodeMethods[(int)instruction]; + // Execute opcode delegate* via calli (see: C# function pointers https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers) + // Stack, gas, and program counter may be modified by call (also instance variables on the vm) + exceptionType = opcodeMethod(this, ref stack, ref gasAvailable, ref programCounter); - stack.PushUInt256(in result); - break; - } - case Instruction.DIV: - { - gasAvailable -= GasCostOf.Low; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (b.IsZero) - { - stack.PushZero(); - } - else - { - UInt256.Divide(in a, in b, out result); - stack.PushUInt256(in result); - } + // Exit loop if run out of gas + if (gasAvailable < 0) goto OutOfGas; + // Exit loop if exception occurred + if (exceptionType != EvmExceptionType.None) break; + // Exit loop if returning data + if (_returnData is not null) break; - break; - } - case Instruction.SDIV: - { - gasAvailable -= GasCostOf.Low; + if (tracingInstructions) + EndInstructionTrace(gasAvailable); + } - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (b.IsZero) - { - stack.PushZero(); - } - else if (As(ref b) == Int256.MinusOne && a == P255) - { - result = P255; - stack.PushUInt256(in result); - } - else - { - Int256.Divide(in As(ref a), in As(ref b), out As(ref result)); - stack.PushUInt256(in result); - } + if (exceptionType is EvmExceptionType.None or EvmExceptionType.Stop or EvmExceptionType.Revert) + { + UpdateCurrentState(programCounter, gasAvailable, stack.Head); + } + else + { + goto ReturnFailure; + } - break; - } - case Instruction.MOD: - { - gasAvailable -= GasCostOf.Low; + if (exceptionType == EvmExceptionType.Revert) goto Revert; + if (_returnData is not null) goto DataReturn; - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - UInt256.Mod(in a, in b, out result); - stack.PushUInt256(in result); - break; - } - case Instruction.SMOD: - { - gasAvailable -= GasCostOf.Low; + return CallResult.Empty(codeInfo.Version); - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (b.IsZeroOrOne) - { - stack.PushZero(); - } - else - { - As(ref a) - .Mod(in As(ref b), out As(ref result)); - stack.PushUInt256(in result); - } + DataReturn: + if (_returnData is EvmState state) + { + return new CallResult(state); + } + else if (_returnData is EofCodeInfo eofCodeInfo) + { + return new CallResult(eofCodeInfo, _returnDataBuffer, null, codeInfo.Version); + } + return new CallResult(null, (byte[])_returnData, null, codeInfo.Version); + Revert: + return new CallResult(null, (byte[])_returnData, null, codeInfo.Version, shouldRevert: true); + OutOfGas: + exceptionType = EvmExceptionType.OutOfGas; + ReturnFailure: + return GetFailureReturn(gasAvailable, exceptionType); - break; - } - case Instruction.ADDMOD: - { - gasAvailable -= GasCostOf.Mid; + static ReadOnlySpan GetInstructions(ICodeInfo codeInfo) + { + ReadOnlySpan codeBytes = codeInfo.CodeSection.Span; + return MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As(ref MemoryMarshal.GetReference(codeBytes)), + codeBytes.Length); + } - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out c)) goto StackUnderflow; + [DoesNotReturn] + static void ThrowOperationCanceledException() => throw new OperationCanceledException("Cancellation Requested"); - if (c.IsZero) - { - stack.PushZero(); - } - else - { - UInt256.AddMod(a, b, c, out result); - stack.PushUInt256(in result); - } + } - break; - } - case Instruction.MULMOD: - { - gasAvailable -= GasCostOf.Mid; + /* + [SkipLocalsInit] + private unsafe CallResult ExecuteCodeTraced(scoped ref EvmStack stack, long gasAvailable) + { + _returnData = null; + ICodeInfo codeInfo = _vmState.Env.CodeInfo; - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out c)) goto StackUnderflow; + int programCounter = _vmState.ProgramCounter; + _sectionIndex = _vmState.FunctionIndex; - if (c.IsZero) - { - stack.PushZero(); - } - else - { - UInt256.MultiplyMod(in a, in b, in c, out result); - stack.PushUInt256(in result); - } + ReadOnlySpan codeSection = codeInfo.CodeSection.Span; - break; - } - case Instruction.EXP: - { - gasAvailable -= GasCostOf.Exp; + EvmExceptionType exceptionType = EvmExceptionType.None; + bool isRevert = false; +#if DEBUG + DebugTracer? debugger = _txTracer.GetTracer(); +#endif + OpCode[] opcodes = _opcodes; + //bool isCancelable = _txTracer.IsCancelable; + uint codeLength = (uint)codeSection.Length; + while ((uint)programCounter < codeLength) + { +#if DEBUG + debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); +#endif + Instruction instruction = (Instruction)codeSection[programCounter]; - Metrics.ExpOpcode++; + //if (isCancelable && _txTracer.IsCancelled) + //{ + // ThrowOperationCanceledException(); + //} - if (!stack.PopUInt256(out a) || !stack.PopWord256(out bytes)) goto StackUnderflow; + //// Evaluated to constant at compile time and code elided if not tracing + //if (typeof(TTracingInstructions) == typeof(IsTracing)) + // StartInstructionTrace(instruction, _vmState, gasAvailable, programCounter, in stack); - int leadingZeros = bytes.LeadingZerosCount(); - if (leadingZeros != 32) - { - int expSize = 32 - leadingZeros; - gasAvailable -= spec.GetExpByteCost() * expSize; - } - else - { - stack.PushOne(); - break; - } + programCounter++; - if (a.IsZero) - { - stack.PushZero(); - } - else if (a.IsOne) - { - stack.PushOne(); - } - else - { - UInt256.Exp(a, new UInt256(bytes, true), out result); - stack.PushUInt256(in result); - } + OpCode opcode = opcodes[(int)instruction]; + exceptionType = opcode(this, ref stack, ref gasAvailable, ref programCounter); - break; - } - case Instruction.SIGNEXTEND: - { - gasAvailable -= GasCostOf.Low; + if (exceptionType != EvmExceptionType.None) break; + if (gasAvailable < 0) goto OutOfGas; - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (a >= BigInt32) - { - if (!stack.EnsureDepth(1)) goto StackUnderflow; - break; - } + if (_returnData is not null) + { + if (!ReferenceEquals(_returnData, CallResult.BoxedEmpty)) + { + goto DataReturnNoTrace; + } + // Non contract call continue rather than constructing a new frame + _returnData = null; + } - int position = 31 - (int)a; + //if (typeof(TTracingInstructions) == typeof(IsTracing)) + //{ + // EndInstructionTrace(gasAvailable, _vmState.Memory.Size); + //} + } - bytes = stack.PeekWord256(); - sbyte sign = (sbyte)bytes[position]; + if (exceptionType == EvmExceptionType.Stop) goto EmptyReturn; + if (exceptionType == EvmExceptionType.Revert) + { + isRevert = true; + goto DataReturn; + } + if (exceptionType != EvmExceptionType.None) goto ReturnFailure; + goto EmptyReturnNoTrace; - if (sign >= 0) - { - BytesZero32.AsSpan(0, position).CopyTo(bytes[..position]); - } - else - { - BytesMax32.AsSpan(0, position).CopyTo(bytes[..position]); - } + // Common exit errors, goto labels to reduce in loop code duplication and to keep loop body smaller + EmptyReturn: + if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, _vmState.Memory.Size); + EmptyReturnNoTrace: + // Ensure gas is positive before updating state + if (gasAvailable < 0) goto OutOfGas; + UpdateCurrentState(_vmState, programCounter, gasAvailable, stack.Head, _sectionIndex); +#if DEBUG + debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); +#endif + return CallResult.Empty(codeInfo.Version); + DataReturn: + if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, _vmState.Memory.Size); + DataReturnNoTrace: + // Ensure gas is positive before updating state + if (gasAvailable < 0) goto OutOfGas; + UpdateCurrentState(_vmState, programCounter, gasAvailable, stack.Head, _sectionIndex); - // Didn't remove from stack so don't need to push back - break; - } - case Instruction.LT: - { - gasAvailable -= GasCostOf.VeryLow; + if (_returnData is EvmState state) + { + return new CallResult(state); + } + else if (_returnData is EofCodeInfo eofCodeInfo) + { + return new CallResult(eofCodeInfo, _returnDataBuffer, null, codeInfo.Version); + } + return new CallResult(null, (byte[])_returnData, null, codeInfo.Version, shouldRevert: isRevert); - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (a < b) - { - stack.PushOne(); - } - else - { - stack.PushZero(); - } + OutOfGas: + exceptionType = EvmExceptionType.OutOfGas; + goto ReturnFailure; + ReturnFailure: + return GetFailureReturn(gasAvailable, exceptionType); - break; - } - case Instruction.GT: - { - gasAvailable -= GasCostOf.VeryLow; + //[DoesNotReturn] + //static void ThrowOperationCanceledException() => + // throw new OperationCanceledException("Cancellation Requested"); + } + */ - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (a > b) - { - stack.PushOne(); - } - else - { - stack.PushZero(); - } + private CallResult GetFailureReturn(long gasAvailable, EvmExceptionType exceptionType) + { + if (_txTracer.IsTracingInstructions) EndInstructionTraceError(gasAvailable, exceptionType); - break; - } - case Instruction.SLT: - { - gasAvailable -= GasCostOf.VeryLow; + 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, + _ => throw new ArgumentOutOfRangeException(nameof(exceptionType), exceptionType, "") + }; + } - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; + private void UpdateCurrentState(int pc, long gas, int stackHead) + { + EvmState state = _vmState; - if (As(ref a).CompareTo(As(ref b)) < 0) - { - stack.PushOne(); - } - else - { - stack.PushZero(); - } + state.ProgramCounter = pc; + state.GasAvailable = gas; + state.DataStackHead = stackHead; + state.FunctionIndex = _sectionIndex; + } - break; - } - case Instruction.SGT: - { - gasAvailable -= GasCostOf.VeryLow; + private static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) + { + long memoryCost = vmState.Memory.CalculateMemoryCost(in position, length, out bool outOfGas); + if (outOfGas) return false; + return memoryCost == 0L || UpdateGas(memoryCost, ref gasAvailable); + } - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (As(ref a).CompareTo(As(ref b)) > 0) - { - stack.PushOne(); - } - else - { - stack.PushZero(); - } + private static bool Jump(CodeInfo codeinfo, in UInt256 jumpDest, ref int programCounter, in ExecutionEnvironment env) + { + if (jumpDest > int.MaxValue) + { + // https://github.com/NethermindEth/nethermind/issues/140 + // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 0xf435a354924097686ea88dab3aac1dd464e6a3b387c77aeee94145b0fa5a63d2 mainnet + return false; + } - break; - } - case Instruction.EQ: - { - gasAvailable -= GasCostOf.VeryLow; + int jumpDestInt = (int)jumpDest; + if (!codeinfo.ValidateJump(jumpDestInt)) + { + // https://github.com/NethermindEth/nethermind/issues/140 + // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 61363 Ropsten + return false; + } - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (a.Equals(b)) - { - stack.PushOne(); - } - else - { - stack.PushZero(); - } + programCounter = jumpDestInt; + return true; + } - break; - } - case Instruction.ISZERO: - { - gasAvailable -= GasCostOf.VeryLow; + [MethodImpl(MethodImplOptions.NoInlining)] + private void StartInstructionTrace(Instruction instruction, long gasAvailable, int programCounter, in EvmStack stackValue) + { + EvmState vmState = _vmState; + int sectionIndex = _sectionIndex; - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (a.IsZero) - { - stack.PushOne(); - } - else - { - stack.PushZero(); - } - - break; - } - case Instruction.AND: - { - gasAvailable -= GasCostOf.VeryLow; - - ref byte bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 aVec = ReadUnaligned>(ref bytesRef); - - bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 bVec = ReadUnaligned>(ref bytesRef); - - WriteUnaligned(ref stack.PushBytesRef(), Vector256.BitwiseAnd(aVec, bVec)); - break; - } - case Instruction.OR: - { - gasAvailable -= GasCostOf.VeryLow; - - ref byte bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 aVec = ReadUnaligned>(ref bytesRef); - - bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 bVec = ReadUnaligned>(ref bytesRef); - - WriteUnaligned(ref stack.PushBytesRef(), Vector256.BitwiseOr(aVec, bVec)); - break; - } - case Instruction.XOR: - { - gasAvailable -= GasCostOf.VeryLow; - - ref byte bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 aVec = ReadUnaligned>(ref bytesRef); - - bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 bVec = ReadUnaligned>(ref bytesRef); - - WriteUnaligned(ref stack.PushBytesRef(), Vector256.Xor(aVec, bVec)); - break; - } - case Instruction.NOT: - { - gasAvailable -= GasCostOf.VeryLow; - - ref byte bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; - - Vector256 negVec = Vector256.OnesComplement(ReadUnaligned>(ref bytesRef)); - - WriteUnaligned(ref stack.PushBytesRef(), negVec); - break; - } - case Instruction.BYTE: - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - bytes = stack.PopWord256(); - - if (a >= BigInt32) - { - stack.PushZero(); - break; - } - - int adjustedPosition = bytes.Length - 32 + (int)a; - if (adjustedPosition < 0) - { - stack.PushZero(); - } - else - { - stack.PushByte(bytes[adjustedPosition]); - } - - break; - } - case Instruction.KECCAK256: - { - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - gasAvailable -= GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in b, out bool outOfGas); - if (outOfGas) goto OutOfGas; - - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; - - bytes = vmState.Memory.LoadSpan(in a, b); - - // Compute the KECCAK256 directly to the stack slot - KeccakCache.ComputeTo(bytes, out As(ref stack.PushBytesRef())); - break; - } - case Instruction.ADDRESS: - { - gasAvailable -= GasCostOf.Base; - - stack.PushBytes(env.ExecutingAccount.Bytes); - break; - } - case Instruction.BALANCE: - { - gasAvailable -= spec.GetBalanceCost(); - - Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; - - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, address, false, spec)) goto OutOfGas; - - result = _state.GetBalance(address); - stack.PushUInt256(in result); - break; - } - case Instruction.CALLER: - { - gasAvailable -= GasCostOf.Base; - - stack.PushBytes(env.Caller.Bytes); - break; - } - case Instruction.CALLVALUE: - { - gasAvailable -= GasCostOf.Base; - - result = env.Value; - stack.PushUInt256(in result); - break; - } - case Instruction.ORIGIN: - { - gasAvailable -= GasCostOf.Base; - - stack.PushBytes(txCtx.Origin.Bytes); - break; - } - case Instruction.CALLDATALOAD: - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - stack.PushBytes(env.InputData.SliceWithZeroPadding(result, 32)); - break; - } - case Instruction.CALLDATASIZE: - { - gasAvailable -= GasCostOf.Base; - - result = (UInt256)env.InputData.Length; - stack.PushUInt256(in result); - break; - } - case Instruction.CALLDATACOPY: - { - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out result)) goto StackUnderflow; - gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); - if (outOfGas) goto OutOfGas; - - if (!result.IsZero) - { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, in result)) goto OutOfGas; - - slice = env.InputData.SliceWithZeroPadding(b, (int)result); - vmState.Memory.Save(in a, in slice); - if (typeof(TTracingInstructions) == typeof(IsTracing)) - { - _txTracer.ReportMemoryChange(a, slice); - } - } - - break; - } - case Instruction.CODESIZE: - { - gasAvailable -= GasCostOf.Base; - - result = (UInt256)env.CodeInfo.MachineCode.Length; - stack.PushUInt256(in result); - break; - } - case Instruction.CODECOPY: - { - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out result)) goto StackUnderflow; - gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); - if (outOfGas) goto OutOfGas; - - if (!result.IsZero) - { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, result)) goto OutOfGas; - - slice = env.CodeInfo.MachineCode.SliceWithZeroPadding(in b, (int)result); - vmState.Memory.Save(in a, in slice); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportMemoryChange(a, in slice); - } - - break; - } - case Instruction.GASPRICE: - { - gasAvailable -= GasCostOf.Base; - - result = txCtx.GasPrice; - stack.PushUInt256(in result); - break; - } - case Instruction.EXTCODESIZE: - { - gasAvailable -= spec.GetExtCodeCost(); - - Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; - - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, address, false, spec)) goto OutOfGas; - - if (typeof(TTracingInstructions) != typeof(IsTracing) && programCounter < codeSection.Length) - { - bool optimizeAccess = false; - Instruction nextInstruction = (Instruction)codeSection[programCounter]; - // Thrown away result - if (nextInstruction == Instruction.POP) - { - programCounter++; - // Add gas cost for POP - gasAvailable -= GasCostOf.Base; - break; - } - // code.length is zero - if (nextInstruction == Instruction.ISZERO) - { - optimizeAccess = true; - } - // code.length > 0 || code.length == 0 - else if ((nextInstruction == Instruction.GT || nextInstruction == Instruction.EQ) && - stack.PeekUInt256IsZero()) - { - optimizeAccess = true; - if (!stack.PopLimbo()) goto StackUnderflow; - } - - if (optimizeAccess) - { - // EXTCODESIZE ISZERO/GT/EQ peephole optimization. - // In solidity 0.8.1+: `return account.code.length > 0;` - // is is a common pattern to check if address is a contract - // however we can just check the address's loaded CodeHash - // to reduce storage access from trying to load the code - - programCounter++; - // Add gas cost for ISZERO, GT, or EQ - gasAvailable -= GasCostOf.VeryLow; - - // IsContract - bool isCodeLengthNotZero = _state.IsContract(address); - if (nextInstruction == Instruction.GT) - { - // Invert, to IsNotContract - isCodeLengthNotZero = !isCodeLengthNotZero; - } - - if (!isCodeLengthNotZero) - { - stack.PushOne(); - } - else - { - stack.PushZero(); - } - break; - } - } - - InstructionExtCodeSize(address, ref stack, txCtx.CodeInfoRepository, spec); - break; - } - case Instruction.EXTCODECOPY: - { - Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out result)) goto StackUnderflow; - - gasAvailable -= spec.GetExtCodeCost() + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); - if (outOfGas) goto OutOfGas; - - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, address, false, spec)) goto OutOfGas; - - if (!result.IsZero) - { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, result)) goto OutOfGas; - ReadOnlyMemory externalCode = txCtx.CodeInfoRepository.GetCachedCodeInfo(_state, address, false, spec, out Address delegation).MachineCode; - - if (spec.IsEofEnabled && IsEof(externalCode, out _)) - { - slice = EofValidator.MAGIC.SliceWithZeroPadding(b, (int)result); - } - else - { - slice = externalCode.SliceWithZeroPadding(b, (int)result); - } - vmState.Memory.Save(in a, in slice); - - if (typeof(TTracingInstructions) == typeof(IsTracing)) - { - _txTracer.ReportMemoryChange(a, in slice); - } - } - - break; - } - case Instruction.RETURNDATASIZE: - { - if (!spec.ReturnDataOpcodesEnabled) goto InvalidInstruction; - - gasAvailable -= GasCostOf.Base; - - result = (UInt256)_returnDataBuffer.Length; - stack.PushUInt256(in result); - break; - } - case Instruction.RETURNDATACOPY: - { - if (!spec.ReturnDataOpcodesEnabled) goto InvalidInstruction; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out c)) goto StackUnderflow; - gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in c, out bool outOfGas); - if (outOfGas) goto OutOfGas; - - - if (env.CodeInfo.Version == 0 && (UInt256.AddOverflow(c, b, out result) || result > _returnDataBuffer.Length)) - { - goto AccessViolation; - } - - if (!c.IsZero) - { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, c)) goto OutOfGas; - - slice = _returnDataBuffer.Span.SliceWithZeroPadding(b, (int)c); - vmState.Memory.Save(in a, in slice); - if (typeof(TTracingInstructions) == typeof(IsTracing)) - { - _txTracer.ReportMemoryChange(a, in slice); - } - } - break; - } - case Instruction.RETURNDATALOAD: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - goto InvalidInstruction; - - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - - slice = _returnDataBuffer.Span.SliceWithZeroPadding(a, 32); - stack.PushBytes(slice); - break; - } - case Instruction.BLOCKHASH: - { - Metrics.BlockhashOpcode++; - - gasAvailable -= GasCostOf.BlockHash; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - long number = a.ToLong(); - - Hash256? blockHash = _blockhashProvider.GetBlockhash(blkCtx.Header, number); - - stack.PushBytes(blockHash is not null ? blockHash.Bytes : BytesZero32); - - if (typeof(TLogger) == typeof(IsTracing)) - { - if (_txTracer.IsTracingBlockHash && blockHash is not null) - { - _txTracer.ReportBlockHash(blockHash); - } - } - - break; - } - case Instruction.COINBASE: - { - gasAvailable -= GasCostOf.Base; - - stack.PushBytes(blkCtx.Header.GasBeneficiary.Bytes); - break; - } - case Instruction.PREVRANDAO: - { - gasAvailable -= GasCostOf.Base; - - if (blkCtx.Header.IsPostMerge) - { - stack.PushBytes(blkCtx.Header.Random.Bytes); - } - else - { - result = blkCtx.Header.Difficulty; - stack.PushUInt256(in result); - } - break; - } - case Instruction.TIMESTAMP: - { - gasAvailable -= GasCostOf.Base; - - result = blkCtx.Header.Timestamp; - stack.PushUInt256(in result); - break; - } - case Instruction.NUMBER: - { - gasAvailable -= GasCostOf.Base; - - result = (UInt256)blkCtx.Header.Number; - stack.PushUInt256(in result); - break; - } - case Instruction.GASLIMIT: - { - gasAvailable -= GasCostOf.Base; - - result = (UInt256)blkCtx.Header.GasLimit; - stack.PushUInt256(in result); - break; - } - case Instruction.CHAINID: - { - if (!spec.ChainIdOpcodeEnabled) goto InvalidInstruction; - - gasAvailable -= GasCostOf.Base; - - stack.PushBytes(_chainId); - break; - } - case Instruction.SELFBALANCE: - { - if (!spec.SelfBalanceOpcodeEnabled) goto InvalidInstruction; - - gasAvailable -= GasCostOf.SelfBalance; - - result = _state.GetBalance(env.ExecutingAccount); - stack.PushUInt256(in result); - break; - } - case Instruction.BASEFEE: - { - if (!spec.BaseFeeEnabled) goto InvalidInstruction; - - gasAvailable -= GasCostOf.Base; - - result = blkCtx.Header.BaseFeePerGas; - stack.PushUInt256(in result); - break; - } - case Instruction.BLOBHASH: - { - if (!spec.IsEip4844Enabled) goto InvalidInstruction; - - gasAvailable -= GasCostOf.BlobHash; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - - if (txCtx.BlobVersionedHashes is not null && result < txCtx.BlobVersionedHashes.Length) - { - stack.PushBytes(txCtx.BlobVersionedHashes[result.u0]); - } - else - { - stack.PushZero(); - } - break; - } - case Instruction.BLOBBASEFEE: - { - if (!spec.BlobBaseFeeEnabled || !blkCtx.BlobBaseFee.HasValue) goto InvalidInstruction; - - gasAvailable -= GasCostOf.Base; - - result = blkCtx.BlobBaseFee.Value; - stack.PushUInt256(in result); - break; - } - case Instruction.POP: - { - gasAvailable -= GasCostOf.Base; - - if (!stack.PopLimbo()) goto StackUnderflow; - break; - } - case Instruction.MLOAD: - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; - bytes = vmState.Memory.LoadSpan(in result); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportMemoryChange(result, bytes); - - stack.PushBytes(bytes); - break; - } - case Instruction.MSTORE: - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - - bytes = stack.PopWord256(); - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; - vmState.Memory.SaveWord(in result, bytes); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportMemoryChange(result, bytes); - - break; - } - case Instruction.MSTORE8: - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - byte data = stack.PopByte(); - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, UInt256.One)) goto OutOfGas; - vmState.Memory.SaveByte(in result, data); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportMemoryChange(result, data); - - break; - } - case Instruction.SLOAD: - { - exceptionType = InstructionSLoad(vmState, ref stack, ref gasAvailable, spec); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - break; - } - case Instruction.SSTORE: - { - exceptionType = InstructionSStore(vmState, ref stack, ref gasAvailable, spec); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - break; - } - case Instruction.JUMP: - { - gasAvailable -= GasCostOf.Mid; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - if (!Jump(env.CodeInfo as CodeInfo, result, ref programCounter, in env)) goto InvalidJumpDestination; - break; - } - case Instruction.JUMPI: - { - gasAvailable -= GasCostOf.High; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - bytes = stack.PopWord256(); - if (!bytes.SequenceEqual(BytesZero32)) - { - if (!Jump(env.CodeInfo as CodeInfo, result, ref programCounter, in env)) goto InvalidJumpDestination; - } - - break; - } - case Instruction.PC: - { - gasAvailable -= GasCostOf.Base; - - stack.PushUInt32(programCounter - 1); - break; - } - case Instruction.MSIZE: - { - gasAvailable -= GasCostOf.Base; - - result = vmState.Memory.Size; - stack.PushUInt256(in result); - break; - } - case Instruction.GAS: - { - gasAvailable -= GasCostOf.Base; - // Ensure gas is positive before pushing to stack - if (gasAvailable < 0) goto OutOfGas; - - result = (UInt256)gasAvailable; - stack.PushUInt256(in result); - break; - } - case Instruction.JUMPDEST: - { - gasAvailable -= GasCostOf.JumpDest; - - break; - } - case Instruction.PUSH0: - { - if (!spec.IncludePush0Instruction) goto InvalidInstruction; - gasAvailable -= GasCostOf.Base; - - stack.PushZero(); - break; - } - case Instruction.PUSH1: - { - gasAvailable -= GasCostOf.VeryLow; - - if (programCounter >= codeSection.Length) - { - stack.PushZero(); - } - else - { - stack.PushByte(codeSection[programCounter]); - } - - programCounter++; - break; - } - case Instruction.PUSH2: - case Instruction.PUSH3: - case Instruction.PUSH4: - case Instruction.PUSH5: - case Instruction.PUSH6: - case Instruction.PUSH7: - case Instruction.PUSH8: - case Instruction.PUSH9: - case Instruction.PUSH10: - case Instruction.PUSH11: - case Instruction.PUSH12: - case Instruction.PUSH13: - case Instruction.PUSH14: - case Instruction.PUSH15: - case Instruction.PUSH16: - case Instruction.PUSH17: - case Instruction.PUSH18: - case Instruction.PUSH19: - case Instruction.PUSH20: - case Instruction.PUSH21: - case Instruction.PUSH22: - case Instruction.PUSH23: - case Instruction.PUSH24: - case Instruction.PUSH25: - case Instruction.PUSH26: - case Instruction.PUSH27: - case Instruction.PUSH28: - case Instruction.PUSH29: - case Instruction.PUSH30: - case Instruction.PUSH31: - case Instruction.PUSH32: - { - gasAvailable -= GasCostOf.VeryLow; - - int length = instruction - Instruction.PUSH1 + 1; - int usedFromCode = Math.Min(codeSection.Length - programCounter, length); - stack.PushLeftPaddedBytes(codeSection.Slice(programCounter, usedFromCode), length); - - programCounter += length; - break; - } - case Instruction.DUP1: - case Instruction.DUP2: - case Instruction.DUP3: - case Instruction.DUP4: - case Instruction.DUP5: - case Instruction.DUP6: - case Instruction.DUP7: - case Instruction.DUP8: - case Instruction.DUP9: - case Instruction.DUP10: - case Instruction.DUP11: - case Instruction.DUP12: - case Instruction.DUP13: - case Instruction.DUP14: - case Instruction.DUP15: - case Instruction.DUP16: - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.Dup(instruction - Instruction.DUP1 + 1)) goto StackUnderflow; - break; - } - case Instruction.DUPN: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - goto InvalidInstruction; - - if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) - goto OutOfGas; - - byte imm = codeSection[programCounter]; - stack.Dup(imm + 1); - - programCounter += 1; - break; - } - case Instruction.SWAP1: - case Instruction.SWAP2: - case Instruction.SWAP3: - case Instruction.SWAP4: - case Instruction.SWAP5: - case Instruction.SWAP6: - case Instruction.SWAP7: - case Instruction.SWAP8: - case Instruction.SWAP9: - case Instruction.SWAP10: - case Instruction.SWAP11: - case Instruction.SWAP12: - case Instruction.SWAP13: - case Instruction.SWAP14: - case Instruction.SWAP15: - case Instruction.SWAP16: - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.Swap(instruction - Instruction.SWAP1 + 2)) goto StackUnderflow; - break; - } - case Instruction.SWAPN: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - goto InvalidInstruction; - - if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) - goto OutOfGas; - - int n = 1 + codeSection[programCounter]; - if (!stack.Swap(n + 1)) goto StackUnderflow; - - programCounter += 1; - break; - } - case Instruction.EXCHANGE: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - goto InvalidInstruction; - - if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) - goto OutOfGas; - - int n = 1 + (int)(codeSection[programCounter] >> 0x04); - int m = 1 + (int)(codeSection[programCounter] & 0x0f); - - stack.Exchange(n + 1, m + n + 1); - - programCounter += 1; - break; - } - case Instruction.LOG0: - case Instruction.LOG1: - case Instruction.LOG2: - case Instruction.LOG3: - case Instruction.LOG4: - { - if (vmState.IsStatic) goto StaticCallViolation; - - exceptionType = InstructionLog(vmState, ref stack, ref gasAvailable, instruction); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - break; - } - case Instruction.CREATE: - case Instruction.CREATE2: - { - Metrics.IncrementCreates(); - if (!spec.Create2OpcodeEnabled && instruction == Instruction.CREATE2) goto InvalidInstruction; - - if (vmState.IsStatic) goto StaticCallViolation; - - (exceptionType, returnData) = InstructionCreate(vmState, ref stack, ref gasAvailable, spec, instruction); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - if (returnData is null) break; - - goto DataReturnNoTrace; - } - case Instruction.EOFCREATE: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - { - goto InvalidInstruction; - } - - if (vmState.IsStatic) goto StaticCallViolation; - - (exceptionType, returnData) = InstructionEofCreate(vmState, ref programCounter, ref codeSection, ref stack, ref gasAvailable, spec, instruction); - - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - if (returnData is null) break; - - goto DataReturnNoTrace; - } - case Instruction.RETURN: - { - if (vmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) - { - goto InvalidInstruction; - } - exceptionType = InstructionReturn(vmState, ref stack, ref gasAvailable, out returnData); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - goto DataReturn; - } - case Instruction.CALL: - case Instruction.CALLCODE: - case Instruction.DELEGATECALL: - case Instruction.STATICCALL: - { - exceptionType = InstructionCall(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - if (returnData is null) - { - break; - } - if (ReferenceEquals(returnData, CallResult.BoxedEmpty)) - { - // Non contract call continue rather than constructing a new frame - continue; - } - - goto DataReturn; - } - case Instruction.REVERT: - { - if (!spec.RevertOpcodeEnabled) goto InvalidInstruction; - - exceptionType = InstructionRevert(vmState, ref stack, ref gasAvailable, out returnData); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - isRevert = true; - goto DataReturn; - } - case Instruction.INVALID: - { - gasAvailable -= GasCostOf.High; - - goto InvalidInstruction; - } - case Instruction.SELFDESTRUCT: - { - if (vmState.IsStatic) goto StaticCallViolation; - - if (spec.UseShanghaiDDosProtection) - { - gasAvailable -= GasCostOf.SelfDestructEip150; - } - - exceptionType = InstructionSelfDestruct(vmState, ref stack, ref gasAvailable, spec); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - goto EmptyReturn; - } - case Instruction.SHL: - { - if (!spec.ShiftOpcodesEnabled) goto InvalidInstruction; - - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (a >= 256UL) - { - if (!stack.PopLimbo()) goto StackUnderflow; - stack.PushZero(); - } - else - { - if (!stack.PopUInt256(out b)) goto StackUnderflow; - result = b << (int)a.u0; - stack.PushUInt256(in result); - } - - break; - } - case Instruction.SHR: - { - if (!spec.ShiftOpcodesEnabled) goto InvalidInstruction; - - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (a >= 256) - { - if (!stack.PopLimbo()) goto StackUnderflow; - stack.PushZero(); - } - else - { - if (!stack.PopUInt256(out b)) goto StackUnderflow; - result = b >> (int)a.u0; - stack.PushUInt256(in result); - } - - break; - } - case Instruction.SAR: - { - if (!spec.ShiftOpcodesEnabled) goto InvalidInstruction; - - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (a >= BigInt256) - { - if (As(ref b).Sign >= 0) - { - stack.PushZero(); - } - else - { - stack.PushSignedInt256(in Int256.MinusOne); - } - } - else - { - As(ref b).RightShift((int)a, out As(ref result)); - stack.PushUInt256(in result); - } - - break; - } - case Instruction.EXTCODEHASH: - { - if (!spec.ExtCodeHashOpcodeEnabled) goto InvalidInstruction; - - gasAvailable -= spec.GetExtCodeHashCost(); - - Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, address, false, spec)) goto OutOfGas; - - if (_state.IsDeadAccount(address)) - { - stack.PushZero(); - } - else - { - if (spec.IsEofEnabled) - { - Memory code = _state.GetCode(address); - if (IsEof(code, out _)) - { - stack.PushBytes(EofHash256); - break; - } - } - - stack.PushBytes(_state.GetCodeHash(address).Bytes); - } - - break; - } - case Instruction.TLOAD: - { - if (!spec.TransientStorageEnabled) goto InvalidInstruction; - - Metrics.TloadOpcode++; - gasAvailable -= GasCostOf.TLoad; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - storageCell = new(env.ExecutingAccount, result); - - ReadOnlySpan value = _state.GetTransientState(in storageCell); - stack.PushBytes(value); - - if (typeof(TTracingStorage) == typeof(IsTracing)) - { - if (gasAvailable < 0) goto OutOfGas; - _txTracer.LoadOperationTransientStorage(storageCell.Address, result, value); - } - - break; - } - case Instruction.TSTORE: - { - if (!spec.TransientStorageEnabled) goto InvalidInstruction; - { - Metrics.TstoreOpcode++; - - if (vmState.IsStatic) goto StaticCallViolation; - - gasAvailable -= GasCostOf.TStore; - - if (!stack.PopUInt256(out result)) goto StackUnderflow; - storageCell = new(env.ExecutingAccount, result); - bytes = stack.PopWord256(); - - _state.SetTransientState(in storageCell, !bytes.IsZero() ? bytes.ToArray() : BytesZero32); - - if (typeof(TTracingStorage) == typeof(IsTracing)) - { - if (gasAvailable < 0) goto OutOfGas; - ReadOnlySpan currentValue = _state.GetTransientState(in storageCell); - _txTracer.SetOperationTransientStorage(storageCell.Address, result, bytes, currentValue); - } - - break; - } - } - case Instruction.MCOPY: - { - if (!spec.MCopyIncluded) goto InvalidInstruction; - { - Metrics.MCopyOpcode++; - - if (!stack.PopUInt256(out a)) goto StackUnderflow; - if (!stack.PopUInt256(out b)) goto StackUnderflow; - if (!stack.PopUInt256(out c)) goto StackUnderflow; - - gasAvailable -= GasCostOf.VeryLow + GasCostOf.VeryLow * EvmPooledMemory.Div32Ceiling(c, out bool outOfGas); - if (outOfGas) goto OutOfGas; - if (!UpdateMemoryCost(vmState, ref gasAvailable, UInt256.Max(b, a), c)) goto OutOfGas; - - bytes = vmState.Memory.LoadSpan(in b, c); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportMemoryChange(b, bytes); - - vmState.Memory.Save(in a, bytes); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportMemoryChange(a, bytes); - - break; - } - } - case Instruction.RJUMP: - { - if (spec.IsEofEnabled && env.CodeInfo.Version > 0) - { - if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) goto OutOfGas; - short offset = codeSection.Slice(programCounter, TWO_BYTE_LENGTH).ReadEthInt16(); - programCounter += TWO_BYTE_LENGTH + offset; - break; - } - goto InvalidInstruction; - } - case Instruction.RJUMPI: - { - if (spec.IsEofEnabled && env.CodeInfo.Version > 0) - { - if (!UpdateGas(GasCostOf.RJumpi, ref gasAvailable)) goto OutOfGas; - Span condition = stack.PopWord256(); - short offset = codeSection.Slice(programCounter, TWO_BYTE_LENGTH).ReadEthInt16(); - if (!condition.SequenceEqual(BytesZero32)) - { - programCounter += offset; - } - programCounter += TWO_BYTE_LENGTH; - break; - } - goto InvalidInstruction; - } - case Instruction.RJUMPV: - { - if (spec.IsEofEnabled && env.CodeInfo.Version > 0) - { - if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; - - stack.PopUInt256(out a); - var count = codeSection[programCounter] + 1; - var immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); - if (a < count) - { - int case_v = programCounter + ONE_BYTE_LENGTH + (int)a * TWO_BYTE_LENGTH; - int offset = codeSection.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); - programCounter += offset; - } - programCounter += immediates; - break; - } - goto InvalidInstruction; - } - case Instruction.CALLF: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - { - goto InvalidInstruction; - } - - if (!UpdateGas(GasCostOf.Callf, ref gasAvailable)) goto OutOfGas; - var index = (int)codeSection.Slice(programCounter, TWO_BYTE_LENGTH).ReadEthUInt16(); - (int inputCount, _, int maxStackHeight) = env.CodeInfo.GetSectionMetadata(index); - - if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) - { - goto StackOverflow; - } - - if (vmState.ReturnStackHead == Eof1.RETURN_STACK_MAX_HEIGHT) - goto InvalidSubroutineEntry; - - vmState.ReturnStack[vmState.ReturnStackHead++] = new EvmState.ReturnState - { - Index = sectionIndex, - Height = stack.Head - inputCount, - Offset = programCounter + TWO_BYTE_LENGTH - }; - - sectionIndex = index; - programCounter = env.CodeInfo.CodeSectionOffset(index).Start; - break; - } - - case Instruction.JUMPF: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - { - goto InvalidInstruction; - } - - if (!UpdateGas(GasCostOf.Jumpf, ref gasAvailable)) goto OutOfGas; - var index = (int)codeSection.Slice(programCounter, TWO_BYTE_LENGTH).ReadEthUInt16(); - (int inputCount, _, int maxStackHeight) = env.CodeInfo.GetSectionMetadata(index); - - if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) - { - goto StackOverflow; - } - - sectionIndex = index; - programCounter = env.CodeInfo.CodeSectionOffset(index).Start; - break; - } - case Instruction.RETF: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - { - goto InvalidInstruction; - } - - if (!UpdateGas(GasCostOf.Retf, ref gasAvailable)) goto OutOfGas; - (_, int outputCount, _) = env.CodeInfo.GetSectionMetadata(sectionIndex); - - var stackFrame = vmState.ReturnStack[--vmState.ReturnStackHead]; - sectionIndex = stackFrame.Index; - programCounter = stackFrame.Offset; - break; - } - case Instruction.EXTCALL: - case Instruction.EXTDELEGATECALL: - case Instruction.EXTSTATICCALL: - { - exceptionType = InstructionEofCall(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - - if (returnData is null) - { - break; - } - if (ReferenceEquals(returnData, CallResult.BoxedEmpty)) - { - // Result pushed to stack - continue; - } - - goto DataReturn; - } - case Instruction.RETURNCONTRACT: - { - if (!spec.IsEofEnabled || !vmState.ExecutionType.IsAnyCreateEof()) - goto InvalidInstruction; - - if (!UpdateGas(GasCostOf.ReturnContract, ref gasAvailable)) goto OutOfGas; - - byte sectionIdx = codeSection[programCounter++]; - ReadOnlyMemory deployCode = env.CodeInfo.ContainerSection[(Range)env.CodeInfo.ContainerSectionOffset(sectionIdx)]; - EofCodeInfo deploycodeInfo = (EofCodeInfo)CodeInfoFactory.CreateCodeInfo(deployCode, spec, EvmObjectFormat.ValidationStrategy.ExractHeader); - - stack.PopUInt256(out a); - stack.PopUInt256(out b); - ReadOnlyMemory auxData = ReadOnlyMemory.Empty; - - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) goto OutOfGas; - - int projectedNewSize = (int)b + deploycodeInfo.DataSection.Length; - if (projectedNewSize < deploycodeInfo.EofContainer.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) - { - goto AccessViolation; - } - - auxData = vmState.Memory.Load(a, b); - - UpdateCurrentState(vmState, programCounter, gasAvailable, stack.Head, sectionIndex); - return new CallResult(deploycodeInfo, auxData, null, env.CodeInfo.Version); - } - case Instruction.DATASIZE: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - goto InvalidInstruction; - - if (!UpdateGas(GasCostOf.DataSize, ref gasAvailable)) goto OutOfGas; - - stack.PushUInt32(dataSection.Length); - break; - } - case Instruction.DATALOAD: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - goto InvalidInstruction; - - if (!UpdateGas(GasCostOf.DataLoad, ref gasAvailable)) goto OutOfGas; - - stack.PopUInt256(out a); - ZeroPaddedSpan zpbytes = dataSection.SliceWithZeroPadding(a, 32); - stack.PushBytes(zpbytes); - break; - } - case Instruction.DATALOADN: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - goto InvalidInstruction; - - if (!UpdateGas(GasCostOf.DataLoadN, ref gasAvailable)) goto OutOfGas; - - var offset = codeSection.Slice(programCounter, TWO_BYTE_LENGTH).ReadEthUInt16(); - ZeroPaddedSpan zpbytes = dataSection.SliceWithZeroPadding(offset, 32); - stack.PushBytes(zpbytes); - - programCounter += TWO_BYTE_LENGTH; - break; - } - case Instruction.DATACOPY: - { - if (!spec.IsEofEnabled || env.CodeInfo.Version == 0) - goto InvalidInstruction; - - stack.PopUInt256(out UInt256 memOffset); - stack.PopUInt256(out UInt256 offset); - stack.PopUInt256(out UInt256 size); - - if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable)) - goto OutOfGas; - - if (size > UInt256.Zero) - { - if (!UpdateMemoryCost(vmState, ref gasAvailable, in memOffset, size)) - goto OutOfGas; - - ZeroPaddedSpan dataSectionSlice = dataSection.SliceWithZeroPadding(offset, (int)size); - vmState.Memory.Save(in memOffset, dataSectionSlice); - if (_txTracer.IsTracingInstructions) - { - _txTracer.ReportMemoryChange((long)memOffset, dataSectionSlice.ToArray().AsSpan()); - } - } - - break; - } - default: - { - goto InvalidInstruction; - } - } - - if (gasAvailable < 0) - { - goto OutOfGas; - } - - if (typeof(TTracingInstructions) == typeof(IsTracing)) - { - EndInstructionTrace(gasAvailable, vmState.Memory.Size); - } - } - - goto EmptyReturnNoTrace; - - // Common exit errors, goto labels to reduce in loop code duplication and to keep loop body smaller - EmptyReturn: - if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState.Memory.Size); - EmptyReturnNoTrace: - // Ensure gas is positive before updating state - if (gasAvailable < 0) goto OutOfGas; - UpdateCurrentState(vmState, programCounter, gasAvailable, stack.Head, sectionIndex); -#if DEBUG - debugger?.TryWait(ref vmState, ref programCounter, ref gasAvailable, ref stack.Head); -#endif - return CallResult.Empty(env.CodeInfo.Version); - DataReturn: - if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState.Memory.Size); - DataReturnNoTrace: - // Ensure gas is positive before updating state - if (gasAvailable < 0) goto OutOfGas; - UpdateCurrentState(vmState, programCounter, gasAvailable, stack.Head, sectionIndex); - - if (returnData is EvmState state) - { - return new CallResult(state); - } - return new CallResult((byte[])returnData, null, env.CodeInfo.Version, shouldRevert: isRevert); - - OutOfGas: - exceptionType = EvmExceptionType.OutOfGas; - goto ReturnFailure; - InvalidInstruction: - exceptionType = EvmExceptionType.BadInstruction; - goto ReturnFailure; - StaticCallViolation: - exceptionType = EvmExceptionType.StaticCallViolation; - goto ReturnFailure; - StackUnderflow: - exceptionType = EvmExceptionType.StackUnderflow; - goto ReturnFailure; - InvalidJumpDestination: - exceptionType = EvmExceptionType.InvalidJumpDestination; - goto ReturnFailure; - AccessViolation: - exceptionType = EvmExceptionType.AccessViolation; - ReturnFailure: - return GetFailureReturn(gasAvailable, exceptionType); - InvalidSubroutineEntry: - exceptionType = EvmExceptionType.InvalidSubroutineEntry; - goto ReturnFailure; - StackOverflow: - exceptionType = EvmExceptionType.StackOverflow; - goto ReturnFailure; - [DoesNotReturn] - static void ThrowOperationCanceledException() => - throw new OperationCanceledException("Cancellation Requested"); - } - - [SkipLocalsInit] - [MethodImpl(MethodImplOptions.NoInlining)] - private void InstructionExtCodeSize(Address address, ref EvmStack stack, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec) where TTracingInstructions : struct, IIsTracing - { - ReadOnlyMemory accountCode = codeInfoRepository.GetCachedCodeInfo(_state, address, false, spec, out Address delegation).MachineCode; - if (spec.IsEofEnabled && IsEof(accountCode, out _)) - { - stack.PushUInt256(2); - } - else - { - UInt256 result = (UInt256)accountCode.Length; - stack.PushUInt256(in result); - } - } - - [SkipLocalsInit] - private EvmExceptionType InstructionCall(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, - Instruction instruction, out object returnData) - where TTracingInstructions : struct, IIsTracing - where TTracingRefunds : struct, IIsTracing - { - Metrics.IncrementCalls(); - - returnData = null; - ref readonly ExecutionEnvironment env = ref vmState.Env; - - if (instruction == Instruction.DELEGATECALL && !spec.DelegateCallEnabled || - instruction == Instruction.STATICCALL && !spec.StaticCallEnabled) return EvmExceptionType.BadInstruction; - - if (!stack.PopUInt256(out UInt256 gasLimit)) return EvmExceptionType.StackUnderflow; - Address codeSource = stack.PopAddress(); - if (codeSource is null) return EvmExceptionType.StackUnderflow; - - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, true, spec)) return EvmExceptionType.OutOfGas; - - UInt256 callValue; - switch (instruction) - { - case Instruction.STATICCALL: - callValue = UInt256.Zero; - break; - case Instruction.DELEGATECALL: - callValue = env.Value; - break; - default: - if (!stack.PopUInt256(out callValue)) return EvmExceptionType.StackUnderflow; - break; - } - - UInt256 transferValue = instruction == Instruction.DELEGATECALL ? UInt256.Zero : callValue; - if (!stack.PopUInt256(out UInt256 dataOffset)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 dataLength)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 outputOffset)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 outputLength)) return EvmExceptionType.StackUnderflow; - - if (vmState.IsStatic && !transferValue.IsZero && instruction != Instruction.CALLCODE) return EvmExceptionType.StaticCallViolation; - - Address caller = instruction == Instruction.DELEGATECALL ? env.Caller : env.ExecutingAccount; - - Address target = instruction == Instruction.CALL || instruction == Instruction.STATICCALL - ? codeSource - : env.ExecutingAccount; - - if (typeof(TLogger) == typeof(IsTracing)) - { - TraceCallDetails(codeSource, ref callValue, ref transferValue, caller, target); - } - - long gasExtra = 0L; - - if (!transferValue.IsZero) - { - gasExtra += GasCostOf.CallValue; - } - - if (!spec.ClearEmptyAccountWhenTouched && !_state.AccountExists(target)) - { - gasExtra += GasCostOf.NewAccount; - } - else if (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && _state.IsDeadAccount(target)) - { - gasExtra += GasCostOf.NewAccount; - } - - if (!UpdateGas(spec.GetCallCost(), ref gasAvailable) || - !UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataLength) || - !UpdateMemoryCost(vmState, ref gasAvailable, in outputOffset, outputLength) || - !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; - - ICodeInfo codeInfo = vmState.Env.TxExecutionContext.CodeInfoRepository.GetCachedCodeInfo(_state, codeSource, spec); - codeInfo.AnalyseInBackgroundIfRequired(); - - if (spec.Use63Over64Rule) - { - gasLimit = UInt256.Min((UInt256)(gasAvailable - gasAvailable / 64), gasLimit); - } - - if (gasLimit >= long.MaxValue) return EvmExceptionType.OutOfGas; - - long gasLimitUl = gasLimit.ToLong(); - if (!UpdateGas(gasLimitUl, ref gasAvailable)) return EvmExceptionType.OutOfGas; - - if (!transferValue.IsZero) - { - if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportExtraGasPressure(GasCostOf.CallStipend); - gasLimitUl += GasCostOf.CallStipend; - } - - if (env.CallDepth >= MaxCallDepth || (!transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue)) - { - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - - if (typeof(TTracingInstructions) == typeof(IsTracing)) - { - // very specific for Parity trace, need to find generalization - very peculiar 32 length... - ReadOnlyMemory? memoryTrace = vmState.Memory.Inspect(in dataOffset, 32); - _txTracer.ReportMemoryChange(dataOffset, memoryTrace is null ? ReadOnlySpan.Empty : memoryTrace.Value.Span); - } - - if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationRemainingGas(gasAvailable); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); - - UpdateGasUp(gasLimitUl, ref gasAvailable); - if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportGasUpdateForVmTrace(gasLimitUl, gasAvailable); - return EvmExceptionType.None; - } - - Snapshot snapshot = _state.TakeSnapshot(); - _state.SubtractFromBalance(caller, transferValue, spec); - - if (codeInfo.IsEmpty && typeof(TTracingInstructions) != typeof(IsTracing) && !_txTracer.IsTracingActions) - { - // Non contract call, no need to construct call frame can just credit balance and return gas - _returnDataBuffer = default; - stack.PushBytes(StatusCode.SuccessBytes.Span); - UpdateGasUp(gasLimitUl, ref gasAvailable); - return FastCall(spec, out returnData, in transferValue, target); - } - - ReadOnlyMemory callData = vmState.Memory.Load(in dataOffset, dataLength); - ExecutionEnvironment callEnv = new - ( - txExecutionContext: in env.TxExecutionContext, - callDepth: env.CallDepth + 1, - caller: caller, - codeSource: codeSource, - executingAccount: target, - transferValue: transferValue, - value: callValue, - inputData: callData, - codeInfo: codeInfo - ); - if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Tx call gas {gasLimitUl}"); - if (outputLength == 0) - { - // TODO: when output length is 0 outputOffset can have any value really - // and the value does not matter and it can cause trouble when beyond long range - outputOffset = 0; - } - - ExecutionType executionType = GetCallExecutionType(instruction, env.IsPostMerge()); - returnData = EvmState.RentFrame( - gasLimitUl, - outputOffset.ToLong(), - outputLength.ToLong(), - executionType, - instruction == Instruction.STATICCALL || vmState.IsStatic, - isCreateOnPreExistingAccount: false, - snapshot: snapshot, - env: callEnv, - stateForAccessLists: vmState.AccessTracker); - - return EvmExceptionType.None; - - EvmExceptionType FastCall(IReleaseSpec spec, out object returnData, in UInt256 transferValue, Address target) - { - _state.AddToBalanceAndCreateIfNotExists(target, transferValue, spec); - Metrics.IncrementEmptyCalls(); - - returnData = CallResult.BoxedEmpty; - return EvmExceptionType.None; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - void TraceCallDetails(Address codeSource, ref UInt256 callValue, ref UInt256 transferValue, Address caller, Address target) - { - _logger.Trace($"caller {caller}"); - _logger.Trace($"code source {codeSource}"); - _logger.Trace($"target {target}"); - _logger.Trace($"value {callValue}"); - _logger.Trace($"transfer value {transferValue}"); - } - } - - [SkipLocalsInit] - private EvmExceptionType InstructionEofCall(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, - Instruction instruction, out object returnData) - where TTracingInstructions : struct, IIsTracing - where TTracingRefunds : struct, IIsTracing - { - Metrics.IncrementCalls(); - - const int MIN_RETAINED_GAS = 5000; - - returnData = null; - ref readonly ExecutionEnvironment env = ref vmState.Env; - - // Instruction is undefined in legacy code and only available in EOF - if (!spec.IsEofEnabled || - env.CodeInfo.Version == 0 || - (instruction == Instruction.EXTDELEGATECALL && !spec.DelegateCallEnabled) || - (instruction == Instruction.EXTSTATICCALL && !spec.StaticCallEnabled)) - return EvmExceptionType.BadInstruction; - - // 1. Pop required arguments from stack, halt with exceptional failure on stack underflow. - stack.PopWord256(out Span targetBytes); - stack.PopUInt256(out UInt256 dataOffset); - stack.PopUInt256(out UInt256 dataLength); - - UInt256 transferValue; - UInt256 callValue; - switch (instruction) - { - case Instruction.EXTSTATICCALL: - transferValue = UInt256.Zero; - callValue = UInt256.Zero; - break; - case Instruction.EXTDELEGATECALL: - transferValue = UInt256.Zero; - callValue = env.Value; - break; - default: // Instruction.EXTCALL - stack.PopUInt256(out transferValue); - callValue = transferValue; - break; - } - - // 3. If value is non-zero: - // a: Halt with exceptional failure if the current frame is in static-mode. - if (vmState.IsStatic && !transferValue.IsZero) return EvmExceptionType.StaticCallViolation; - // b. Charge CALL_VALUE_COST gas. - if (instruction == Instruction.EXTCALL && !transferValue.IsZero && !UpdateGas(GasCostOf.CallValue, ref gasAvailable)) return EvmExceptionType.OutOfGas; - - // 4. If target_address has any of the high 12 bytes set to a non-zero value - // (i.e. it does not contain a 20-byte address) - if (!targetBytes[0..12].IsZero()) - { - // then halt with an exceptional failure. - return EvmExceptionType.AddressOutOfRange; - } - - Address caller = instruction == Instruction.EXTDELEGATECALL ? env.Caller : env.ExecutingAccount; - Address codeSource = new Address(targetBytes[12..].ToArray()); - Address target = instruction == Instruction.EXTDELEGATECALL - ? env.ExecutingAccount - : codeSource; - - // 5. Perform (and charge for) memory expansion using [input_offset, input_size]. - if (!UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, in dataLength)) return EvmExceptionType.OutOfGas; - // 1. Charge WARM_STORAGE_READ_COST (100) gas. - // 6. If target_address is not in the warm_account_list, charge COLD_ACCOUNT_ACCESS - WARM_STORAGE_READ_COST (2500) gas. - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, false, spec)) return EvmExceptionType.OutOfGas; - - if ((!spec.ClearEmptyAccountWhenTouched && !_state.AccountExists(codeSource)) - || (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && _state.IsDeadAccount(codeSource))) - { - // 7. If target_address is not in the state and the call configuration would result in account creation, - // charge ACCOUNT_CREATION_COST (25000) gas. (The only such case in this EIP is if value is non-zero.) - if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; - } - - // 8. Calculate the gas available to callee as caller’s remaining gas reduced by max(floor(gas/64), MIN_RETAINED_GAS). - long callGas = gasAvailable - Math.Max(gasAvailable / 64, MIN_RETAINED_GAS); - - // 9. Fail with status code 1 returned on stack if any of the following is true (only gas charged until this point is consumed): - // a: Gas available to callee at this point is less than MIN_CALLEE_GAS. - // b: Balance of the current account is less than value. - // c: Current call stack depth equals 1024. - if (callGas < GasCostOf.CallStipend || - (!transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) || - env.CallDepth >= MaxCallDepth) - { - returnData = CallResult.BoxedEmpty; - _returnDataBuffer = Array.Empty(); - stack.PushOne(); - - if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); - if (typeof(TTracingInstructions) == typeof(IsTracing)) - { - // very specific for Parity trace, need to find generalization - very peculiar 32 length... - ReadOnlyMemory memoryTrace = 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; - } - - if (typeof(TLogger) == typeof(IsTracing)) - { - _logger.Trace($"caller {caller}"); - _logger.Trace($"target {codeSource}"); - _logger.Trace($"transferValue {transferValue}"); - _logger.Trace($"callValue {callValue}"); - } - - ICodeInfo targetCodeInfo = vmState.Env.TxExecutionContext.CodeInfoRepository.GetCachedCodeInfo(_state, codeSource, spec); - targetCodeInfo.AnalyseInBackgroundIfRequired(); - - if (instruction is Instruction.EXTDELEGATECALL - && targetCodeInfo.Version == 0) - { - // https://github.com/ipsilon/eof/blob/main/spec/eof.md#new-behavior - // EXTDELEGATECALL to a non-EOF contract (legacy contract, EOA, empty account) is disallowed, - // and it returns 1 (same as when the callee frame reverts) to signal failure. - // Only initial gas cost of EXTDELEGATECALL is consumed (similarly to the call depth check) - // and the target address still becomes warm. - returnData = CallResult.BoxedEmpty; - _returnDataBuffer = Array.Empty(); - stack.PushOne(); - return EvmExceptionType.None; - } - - // 10. Perform the call with the available gas and configuration. - if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; - - ReadOnlyMemory callData = vmState.Memory.Load(in dataOffset, dataLength); - - Snapshot snapshot = _state.TakeSnapshot(); - _state.SubtractFromBalance(caller, transferValue, spec); - - ExecutionEnvironment callEnv = new - ( - txExecutionContext: in env.TxExecutionContext, - callDepth: env.CallDepth + 1, - caller: caller, - codeSource: codeSource, - executingAccount: target, - transferValue: transferValue, - value: callValue, - inputData: callData, - codeInfo: targetCodeInfo - ); - if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Tx call gas {callGas}"); - - ExecutionType executionType = GetCallExecutionType(instruction, env.IsPostMerge()); - returnData = EvmState.RentFrame( - callGas, - outputDestination: 0, - outputLength: 0, - executionType, - isStatic: instruction == Instruction.EXTSTATICCALL || vmState.IsStatic, - isCreateOnPreExistingAccount: false, - in snapshot, - env: in callEnv, - in vmState.AccessTracker - ); - - return EvmExceptionType.None; - } - - [SkipLocalsInit] - private static EvmExceptionType InstructionRevert(EvmState vmState, ref EvmStack stack, ref long gasAvailable, out object returnData) - where TTracing : struct, IIsTracing - { - SkipInit(out returnData); - - if (!stack.PopUInt256(out UInt256 position) || - !stack.PopUInt256(out UInt256 length)) - return EvmExceptionType.StackUnderflow; - - if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, in length)) - { - return EvmExceptionType.OutOfGas; - } - - returnData = vmState.Memory.Load(in position, in length).ToArray(); - return EvmExceptionType.None; - } - - [SkipLocalsInit] - private static EvmExceptionType InstructionReturn(EvmState vmState, ref EvmStack stack, ref long gasAvailable, out object returnData) - where TTracing : struct, IIsTracing - { - SkipInit(out returnData); - - if (!stack.PopUInt256(out UInt256 position) || - !stack.PopUInt256(out UInt256 length)) - return EvmExceptionType.StackUnderflow; - - if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, in length)) - { - return EvmExceptionType.OutOfGas; - } - - returnData = vmState.Memory.Load(in position, in length).ToArray(); - - return EvmExceptionType.None; - } - - [SkipLocalsInit] - private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec) - where TTracing : struct, IIsTracing - { - Metrics.IncrementSelfDestructs(); - - Address inheritor = stack.PopAddress(); - if (inheritor is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, false, spec, false)) return EvmExceptionType.OutOfGas; - - Address executingAccount = vmState.Env.ExecutingAccount; - bool createInSameTx = vmState.AccessTracker.CreateList.Contains(executingAccount); - if (!spec.SelfdestructOnlyOnSameTransaction || createInSameTx) - vmState.AccessTracker.ToBeDestroyed(executingAccount); - - UInt256 result = _state.GetBalance(executingAccount); - if (_txTracer.IsTracingActions) _txTracer.ReportSelfDestruct(executingAccount, result, inheritor); - if (spec.ClearEmptyAccountWhenTouched && !result.IsZero && _state.IsDeadAccount(inheritor)) - { - if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; - } - - bool inheritorAccountExists = _state.AccountExists(inheritor); - if (!spec.ClearEmptyAccountWhenTouched && !inheritorAccountExists && spec.UseShanghaiDDosProtection) - { - if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; - } - - if (!inheritorAccountExists) - { - _state.CreateAccount(inheritor, result); - } - else if (!inheritor.Equals(executingAccount)) - { - _state.AddToBalance(inheritor, result, spec); - } - - if (spec.SelfdestructOnlyOnSameTransaction && !createInSameTx && inheritor.Equals(executingAccount)) - return EvmExceptionType.None; // don't burn eth when contract is not destroyed per EIP clarification - - _state.SubtractFromBalance(executingAccount, result, spec); - return EvmExceptionType.None; - } - - [SkipLocalsInit] - private (EvmExceptionType exceptionType, EvmState? callState) InstructionEofCreate(EvmState vmState, ref int pc, ref ReadOnlySpan codeSection, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) - where TTracing : struct, IIsTracing - { - ref readonly ExecutionEnvironment env = ref vmState.Env; - EofCodeInfo container = env.CodeInfo as EofCodeInfo; - var currentContext = ExecutionType.EOFCREATE; - - // 1 - deduct TX_CREATE_COST gas - if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) - return (EvmExceptionType.OutOfGas, null); - - // 2 - read immediate operand initcontainer_index, encoded as 8-bit unsigned value - int initcontainerIndex = codeSection[pc++]; - - // 3 - pop value, salt, input_offset, input_size from the operand stack - // no stack checks becaue EOF guarantees no stack undeflows - stack.PopUInt256(out UInt256 value); - stack.PopWord256(out Span salt); - stack.PopUInt256(out UInt256 dataOffset); - stack.PopUInt256(out UInt256 dataSize); - - // 4 - perform (and charge for) memory expansion using [input_offset, input_size] - if (!UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataSize)) return (EvmExceptionType.OutOfGas, null); - - // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed - // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header - ReadOnlySpan initContainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initcontainerIndex).Value]; - // Eip3860 - if (spec.IsEip3860Enabled) - { - //if (!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) - // return (EvmExceptionType.OutOfGas, null); - if (initContainer.Length > spec.MaxInitCodeSize) return (EvmExceptionType.OutOfGas, null); - } - - // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) - long numberOfWordsInInitCode = EvmPooledMemory.Div32Ceiling((UInt256)initContainer.Length); - long hashCost = GasCostOf.Sha3Word * numberOfWordsInInitCode; - if (!UpdateGas(hashCost, ref gasAvailable)) - return (EvmExceptionType.OutOfGas, null); - - // 7 - check that current call depth is below STACK_DEPTH_LIMIT and that caller balance is enough to transfer value - // in case of failure return 0 on the stack, caller’s nonce is not updated and gas for initcode execution is not consumed. - UInt256 balance = _state.GetBalance(env.ExecutingAccount); - if (env.CallDepth >= MaxCallDepth || value > balance) - { - // TODO: need a test for this - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - - // 8 - caller’s memory slice [input_offset:input_size] is used as calldata - Span calldata = vmState.Memory.LoadSpan(dataOffset, dataSize); - - // 9 - execute the container and deduct gas for execution. The 63/64th rule from EIP-150 applies. - long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; - if (!UpdateGas(callGas, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - - // 10 - increment sender account’s nonce - UInt256 accountNonce = _state.GetNonce(env.ExecutingAccount); - UInt256 maxNonce = ulong.MaxValue; - if (accountNonce >= maxNonce) - { - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - _state.IncrementNonce(env.ExecutingAccount); - - // 11 - calculate new_address as keccak256(0xff || sender || salt || keccak256(initcontainer))[12:] - Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initContainer); - if (spec.UseHotAndColdStorage) - { - // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 - vmState.AccessTracker.WarmUp(contractAddress); - } - - - if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState?.Memory.Size ?? 0); - // todo: === below is a new call - refactor / move - - Snapshot snapshot = _state.TakeSnapshot(); - - bool accountExists = _state.AccountExists(contractAddress); - - if (accountExists && contractAddress.IsNonZeroAccount(spec, vmState.Env.TxExecutionContext.CodeInfoRepository, _state)) - { - /* we get the snapshot before this as there is a possibility with that we will touch an empty account and remove it even if the REVERT operation follows */ - if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - - if (_state.IsDeadAccount(contractAddress)) - { - _state.ClearStorage(contractAddress); - } - - _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - - - ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(initContainer.ToArray(), spec, EvmObjectFormat.ValidationStrategy.ExractHeader); - - ExecutionEnvironment callEnv = new - ( - txExecutionContext: in env.TxExecutionContext, - callDepth: env.CallDepth + 1, - caller: env.ExecutingAccount, - executingAccount: contractAddress, - codeSource: null, - codeInfo: codeInfo, - inputData: calldata.ToArray(), - transferValue: value, - value: value - ); - EvmState callState = EvmState.RentFrame( - callGas, - outputDestination: 0, - outputLength: 0, - executionType: currentContext, - isStatic: vmState.IsStatic, - isCreateOnPreExistingAccount: accountExists, - in snapshot, - env: in callEnv, - in vmState.AccessTracker - ); - - return (EvmExceptionType.None, callState); - } - - [SkipLocalsInit] - private (EvmExceptionType exceptionType, EvmState? callState) InstructionCreate(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) - where TTracing : struct, IIsTracing - { - ref readonly ExecutionEnvironment env = ref vmState.Env; - - // TODO: happens in CREATE_empty000CreateInitCode_Transaction but probably has to be handled differently - if (!_state.AccountExists(env.ExecutingAccount)) - { - _state.CreateAccount(env.ExecutingAccount, UInt256.Zero); - } - - if (!stack.PopUInt256(out UInt256 value) || - !stack.PopUInt256(out UInt256 memoryPositionOfInitCode) || - !stack.PopUInt256(out UInt256 initCodeLength)) - return (EvmExceptionType.StackUnderflow, null); - - Span salt = default; - if (instruction == Instruction.CREATE2) - { - salt = stack.PopWord256(); - } - - //EIP-3860 - if (spec.IsEip3860Enabled) - { - if (initCodeLength > spec.MaxInitCodeSize) return (EvmExceptionType.OutOfGas, null); - } - - bool outOfGas = false; - long gasCost = GasCostOf.Create + - (spec.IsEip3860Enabled ? GasCostOf.InitCodeWord * EvmPooledMemory.Div32Ceiling(initCodeLength, out outOfGas) : 0) + - (instruction == Instruction.CREATE2 - ? GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(initCodeLength, out outOfGas) - : 0); - if (outOfGas || !UpdateGas(gasCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - - if (!UpdateMemoryCost(vmState, ref gasAvailable, in memoryPositionOfInitCode, initCodeLength)) return (EvmExceptionType.OutOfGas, null); - - // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? - if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients - { - // TODO: need a test for this - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - - ReadOnlyMemory initCode = vmState.Memory.Load(in memoryPositionOfInitCode, initCodeLength); - - UInt256 balance = _state.GetBalance(env.ExecutingAccount); - if (value > balance) - { - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - - UInt256 accountNonce = _state.GetNonce(env.ExecutingAccount); - UInt256 maxNonce = ulong.MaxValue; - if (accountNonce >= maxNonce) - { - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - - if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState.Memory.Size); - // todo: === below is a new call - refactor / move - - long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; - if (!UpdateGas(callGas, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - - Address contractAddress = instruction == Instruction.CREATE - ? ContractAddress.From(env.ExecutingAccount, _state.GetNonce(env.ExecutingAccount)) - : ContractAddress.From(env.ExecutingAccount, salt, initCode.Span); - - if (spec.UseHotAndColdStorage) - { - // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 - vmState.AccessTracker.WarmUp(contractAddress); - } - - // Do not add the initCode to the cache as it is - // pointing to data in this tx and will become invalid - // for another tx as returned to pool. - if (spec.IsEofEnabled && initCode.Span.StartsWith(EofValidator.MAGIC)) - { - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - UpdateGasUp(callGas, ref gasAvailable); - return (EvmExceptionType.None, null); - } - - _state.IncrementNonce(env.ExecutingAccount); - - CodeInfoFactory.CreateInitCodeInfo(initCode.ToArray(), spec, out ICodeInfo codeinfo, out _); - codeinfo.AnalyseInBackgroundIfRequired(); - - Snapshot snapshot = _state.TakeSnapshot(); - - bool accountExists = _state.AccountExists(contractAddress); - - if (accountExists && contractAddress.IsNonZeroAccount(spec, env.TxExecutionContext.CodeInfoRepository, _state)) - { - /* we get the snapshot before this as there is a possibility with that we will touch an empty account and remove it even if the REVERT operation follows */ - if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); - _returnDataBuffer = Array.Empty(); - stack.PushZero(); - return (EvmExceptionType.None, null); - } - - if (_state.IsDeadAccount(contractAddress)) - { - _state.ClearStorage(contractAddress); - } - - _state.SubtractFromBalance(env.ExecutingAccount, value, spec); - - ExecutionEnvironment callEnv = new - ( - txExecutionContext: in env.TxExecutionContext, - callDepth: env.CallDepth + 1, - caller: env.ExecutingAccount, - executingAccount: contractAddress, - codeSource: null, - codeInfo: codeinfo, - inputData: default, - transferValue: value, - value: value - ); - EvmState callState = EvmState.RentFrame( - callGas, - outputDestination: 0, - outputLength: 0, - instruction switch - { - Instruction.CREATE => ExecutionType.CREATE, - Instruction.CREATE2 => ExecutionType.CREATE2, - _ => throw new UnreachableException() - }, - isStatic: instruction == Instruction.EXTSTATICCALL || vmState.IsStatic, - isCreateOnPreExistingAccount: accountExists, - in snapshot, - env: in callEnv, - in vmState.AccessTracker - ); - - return (EvmExceptionType.None, callState); - } - - [SkipLocalsInit] - private EvmExceptionType InstructionLog(EvmState vmState, ref EvmStack stack, ref long gasAvailable, Instruction instruction) - where TTracing : struct, IIsTracing - { - if (!stack.PopUInt256(out UInt256 position)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; - long topicsCount = instruction - Instruction.LOG0; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, length)) return EvmExceptionType.OutOfGas; - if (!UpdateGas( - GasCostOf.Log + topicsCount * GasCostOf.LogTopic + - length.ToLong() * GasCostOf.LogData, ref gasAvailable)) return EvmExceptionType.OutOfGas; - - ReadOnlyMemory data = vmState.Memory.Load(in position, length); - Hash256[] topics = new Hash256[topicsCount]; - for (int i = 0; i < topics.Length; i++) - { - topics[i] = new Hash256(stack.PopWord256()); - } - - LogEntry logEntry = new( - vmState.Env.ExecutingAccount, - data.ToArray(), - topics); - vmState.AccessTracker.Logs.Add(logEntry); - - if (_txTracer.IsTracingLogs) - { - _txTracer.ReportLog(logEntry); - } - - return EvmExceptionType.None; - } - - [SkipLocalsInit] - private EvmExceptionType InstructionSLoad(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec) - where TTracingInstructions : struct, IIsTracing - where TTracingStorage : struct, IIsTracing - { - Metrics.IncrementSLoadOpcode(); - gasAvailable -= spec.GetSLoadCost(); - - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - StorageCell storageCell = new(vmState.Env.ExecutingAccount, result); - if (!ChargeStorageAccessGas( - ref gasAvailable, - vmState, - in storageCell, - StorageAccessType.SLOAD, - spec)) return EvmExceptionType.OutOfGas; - - ReadOnlySpan value = _state.Get(in storageCell); - stack.PushBytes(value); - if (typeof(TTracingStorage) == typeof(IsTracing)) - { - _txTracer.LoadOperationStorage(storageCell.Address, result, value); - } - - return EvmExceptionType.None; - } - - [SkipLocalsInit] - private EvmExceptionType InstructionSStore(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec) - where TTracingInstructions : struct, IIsTracing - where TTracingRefunds : struct, IIsTracing - where TTracingStorage : struct, IIsTracing - { - Metrics.IncrementSStoreOpcode(); - - if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; - // fail fast before the first storage read if gas is not enough even for reset - if (!spec.UseNetGasMetering && !UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; - - if (spec.UseNetGasMeteringWithAStipendFix) - { - if (typeof(TTracingRefunds) == typeof(IsTracing)) - _txTracer.ReportExtraGasPressure(GasCostOf.CallStipend - spec.GetNetMeteredSStoreCost() + 1); - if (gasAvailable <= GasCostOf.CallStipend) return EvmExceptionType.OutOfGas; - } - - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - ReadOnlySpan bytes = stack.PopWord256(); - bool newIsZero = bytes.IsZero(); - bytes = !newIsZero ? bytes.WithoutLeadingZeros() : BytesZero; - - StorageCell storageCell = new(vmState.Env.ExecutingAccount, result); - - if (!ChargeStorageAccessGas( - ref gasAvailable, - vmState, - in storageCell, - StorageAccessType.SSTORE, - spec)) return EvmExceptionType.OutOfGas; - - ReadOnlySpan currentValue = _state.Get(in storageCell); - // Console.WriteLine($"current: {currentValue.ToHexString()} newValue {newValue.ToHexString()}"); - bool currentIsZero = currentValue.IsZero(); - - bool newSameAsCurrent = (newIsZero && currentIsZero) || Bytes.AreEqual(currentValue, bytes); - long sClearRefunds = RefundOf.SClear(spec.IsEip3529Enabled); - - if (!spec.UseNetGasMetering) // note that for this case we already deducted 5000 - { - if (newIsZero) - { - if (!newSameAsCurrent) - { - vmState.Refund += sClearRefunds; - if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportRefund(sClearRefunds); - } - } - else if (currentIsZero) - { - if (!UpdateGas(GasCostOf.SSet - GasCostOf.SReset, ref gasAvailable)) return EvmExceptionType.OutOfGas; - } - } - else // net metered - { - if (newSameAsCurrent) - { - if (!UpdateGas(spec.GetNetMeteredSStoreCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; - } - else // net metered, C != N - { - Span originalValue = _state.GetOriginal(in storageCell); - bool originalIsZero = originalValue.IsZero(); - - bool currentSameAsOriginal = Bytes.AreEqual(originalValue, currentValue); - if (currentSameAsOriginal) - { - if (currentIsZero) - { - if (!UpdateGas(GasCostOf.SSet, ref gasAvailable)) return EvmExceptionType.OutOfGas; - } - else // net metered, current == original != new, !currentIsZero - { - if (!UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; - - if (newIsZero) - { - vmState.Refund += sClearRefunds; - if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportRefund(sClearRefunds); - } - } - } - else // net metered, new != current != original - { - long netMeteredStoreCost = spec.GetNetMeteredSStoreCost(); - if (!UpdateGas(netMeteredStoreCost, ref gasAvailable)) return EvmExceptionType.OutOfGas; - - if (!originalIsZero) // net metered, new != current != original != 0 - { - if (currentIsZero) - { - vmState.Refund -= sClearRefunds; - if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportRefund(-sClearRefunds); - } - - if (newIsZero) - { - vmState.Refund += sClearRefunds; - if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportRefund(sClearRefunds); - } - } - - bool newSameAsOriginal = Bytes.AreEqual(originalValue, bytes); - if (newSameAsOriginal) - { - long refundFromReversal; - if (originalIsZero) - { - refundFromReversal = spec.GetSetReversalRefund(); - } - else - { - refundFromReversal = spec.GetClearReversalRefund(); - } - - vmState.Refund += refundFromReversal; - if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportRefund(refundFromReversal); - } - } - } - } - - if (!newSameAsCurrent) - { - _state.Set(in storageCell, newIsZero ? BytesZero : bytes.ToArray()); - - if (typeof(TTracingInstructions) == typeof(IsTracing)) - { - ReadOnlySpan valueToStore = newIsZero ? BytesZero.AsSpan() : bytes; - byte[] storageBytes = new byte[32]; // do not stackalloc here - storageCell.Index.ToBigEndian(storageBytes); - _txTracer.ReportStorageChange(storageBytes, valueToStore); - } - - if (typeof(TTracingStorage) == typeof(IsTracing)) - { - _txTracer.SetOperationStorage(storageCell.Address, result, bytes, currentValue); - } - } - - return EvmExceptionType.None; - } - - private CallResult GetFailureReturn(long gasAvailable, EvmExceptionType exceptionType) - where TTracingInstructions : struct, IIsTracing - { - if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTraceError(gasAvailable, exceptionType); - - 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, - _ => throw new ArgumentOutOfRangeException(nameof(exceptionType), exceptionType, "") - }; - } - - private static void UpdateCurrentState(EvmState state, int pc, long gas, int stackHead, int sectionId) - { - state.ProgramCounter = pc; - state.GasAvailable = gas; - state.DataStackHead = stackHead; - state.FunctionIndex = sectionId; - } - - private static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) - { - long memoryCost = vmState.Memory.CalculateMemoryCost(in position, length, out bool outOfGas); - if (outOfGas) return false; - return memoryCost == 0L || UpdateGas(memoryCost, ref gasAvailable); - } - - private static bool Jump(CodeInfo codeinfo, in UInt256 jumpDest, ref int programCounter, in ExecutionEnvironment env) - { - if (jumpDest > int.MaxValue) - { - // https://github.com/NethermindEth/nethermind/issues/140 - // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 0xf435a354924097686ea88dab3aac1dd464e6a3b387c77aeee94145b0fa5a63d2 mainnet - return false; - } - - int jumpDestInt = (int)jumpDest; - if (!codeinfo.ValidateJump(jumpDestInt)) - { - // https://github.com/NethermindEth/nethermind/issues/140 - // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 61363 Ropsten - return false; - } - - programCounter = jumpDestInt; - return true; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void StartInstructionTrace(Instruction instruction, EvmState vmState, long gasAvailable, int programCounter, int sectionIndex, in EvmStack stackValue) - where TIsTracing : struct, IIsTracing - { bool isEofFrame = vmState.Env.CodeInfo.Version > 0; _txTracer.StartOperation(programCounter, instruction, gasAvailable, vmState.Env, isEofFrame ? sectionIndex : 0, isEofFrame ? vmState.ReturnStackHead + 1 : 0); @@ -3488,13 +1065,13 @@ private void StartInstructionTrace(Instruction instruction, EvmState if (_txTracer.IsTracingStack) { - Memory stackMemory = vmState.DataStack.AsMemory().Slice(0, stackValue.Head * EvmStack.WordSize); + Memory stackMemory = vmState.DataStack.AsMemory().Slice(0, stackValue.Head * EvmStack.WordSize); _txTracer.SetOperationStack(new TraceStack(stackMemory)); } } [MethodImpl(MethodImplOptions.NoInlining)] - private void EndInstructionTrace(long gasAvailable, ulong memorySize) + private void EndInstructionTrace(long gasAvailable) { _txTracer.ReportOperationRemainingGas(gasAvailable); } diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs index f6b70140320..08a7d388031 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Diagnostics.CodeAnalysis; + +using Nethermind.Consensus.Tracing; using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.State; @@ -10,14 +12,14 @@ namespace Nethermind.Facade.Simulate; public class SimulateVirtualMachine(IVirtualMachine virtualMachine) : IVirtualMachine { - public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) where TTracingActions : struct, VirtualMachine.IIsTracing + public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) { - if (typeof(TTracingActions) == typeof(VirtualMachine.IsTracing) && TryGetLogsMutator(txTracer, out ITxLogsMutator logsMutator)) + if (txTracer.IsTracingActions && TryGetLogsMutator(txTracer, out ITxLogsMutator logsMutator)) { logsMutator.SetLogsToMutate(state.AccessTracker.Logs); } - return virtualMachine.Run(state, worldState, txTracer); + return virtualMachine.Run(state, worldState, txTracer); } private static bool TryGetLogsMutator(ITxTracer txTracer, [NotNullWhen(true)] out ITxLogsMutator? txLogsMutator) diff --git a/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs b/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs index fd15b999893..9325f6d591a 100644 --- a/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs +++ b/src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs @@ -70,6 +70,7 @@ private static JsonSerializerOptions CreateOptions(bool indented, IEnumerable _spec.IsEofEnabled; public bool IsEip6110Enabled => _spec.IsEip6110Enabled; public Address DepositContractAddress => _spec.DepositContractAddress; + + public object? EvmInstructions { get => _spec.EvmInstructions; set => _spec.EvmInstructions = value; } } } diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index 6d270634237..7e4fecbd3aa 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -140,5 +140,6 @@ public Address Eip2935ContractAddress get => IsEip2935Enabled ? _eip2935ContractAddress : null; set => _eip2935ContractAddress = value; } + public object EvmInstructions { get; set; } } } From d316e731abe03a32eb01bdc56d437f34b24e3e66 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 31 Jan 2025 19:10:43 +0000 Subject: [PATCH 176/255] Less indirection --- .../Nethermind.Evm/IVirtualMachine.cs | 14 ------- .../Instructions/EvmInstructions.Bitwise.cs | 2 +- .../Instructions/EvmInstructions.Call.cs | 6 +-- .../Instructions/EvmInstructions.CodeCopy.cs | 12 +++--- .../Instructions/EvmInstructions.Create.cs | 2 +- .../EvmInstructions.Environment.cs | 26 ++++++------- .../Instructions/EvmInstructions.Eof.cs | 38 +++++++++---------- .../Instructions/EvmInstructions.Extras.cs | 6 +-- .../Instructions/EvmInstructions.Jump.cs | 8 ++-- .../EvmInstructions.Math1Param.cs | 2 +- .../EvmInstructions.Math2Param.cs | 2 +- .../EvmInstructions.Math3Param.cs | 2 +- .../Instructions/EvmInstructions.Shifts.cs | 6 +-- .../Instructions/EvmInstructions.Stack.cs | 10 ++--- .../Instructions/EvmInstructions.Storage.cs | 12 +++--- .../Instructions/EvmInstructions.cs | 26 ++++++------- .../Nethermind.Evm/VirtualMachine.cs | 17 ++++----- 17 files changed, 86 insertions(+), 105 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs index fb25cdb1230..4d404ccee24 100644 --- a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs @@ -14,18 +14,4 @@ public interface IVirtualMachine { TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer); } - - internal interface IEvm : IVirtualMachine - { - IReleaseSpec Spec { get; } - EvmState State { get; } - ITxTracer TxTracer { get; } - IWorldState WorldState { get; } - ReadOnlySpan ChainId { get; } - ICodeInfoRepository CodeInfoRepository { get; } - ReadOnlyMemory ReturnDataBuffer { get; set; } - IBlockhashProvider BlockhashProvider { get; } - int SectionIndex { get; set; } - object ReturnData { get; set; } - } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs index 81cd0a32ac2..e29c4f3c291 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs @@ -16,7 +16,7 @@ public interface IOpBitwise } [SkipLocalsInit] - public static EvmExceptionType InstructionBitwise(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionBitwise(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpBitwise : struct, IOpBitwise { gasAvailable -= TOpBitwise.GasCost; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 252606881f9..c995f2357f7 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -22,7 +22,7 @@ public interface IOpCall } [SkipLocalsInit] - public static EvmExceptionType InstructionCall(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCall(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCall : struct, IOpCall { Metrics.IncrementCalls(); @@ -177,7 +177,7 @@ public static EvmExceptionType InstructionCall(IEvm vm, ref EvmStack st return EvmExceptionType.None; - EvmExceptionType FastCall(IEvm vm, IReleaseSpec spec, in UInt256 transferValue, Address target) + EvmExceptionType FastCall(VirtualMachine vm, IReleaseSpec spec, in UInt256 transferValue, Address target) { IWorldState state = vm.WorldState; if (!state.AccountExists(target)) @@ -227,7 +227,7 @@ public struct OpStaticCall : IOpCall } [SkipLocalsInit] - public static EvmExceptionType InstructionReturn(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionReturn(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (vm.State.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) { diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 4910f267225..78913f89085 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -14,11 +14,11 @@ internal sealed partial class EvmInstructions { public interface IOpCodeCopy { - abstract static ReadOnlySpan GetCode(IEvm vm); + abstract static ReadOnlySpan GetCode(VirtualMachine vm); } [SkipLocalsInit] - public static EvmExceptionType InstructionCodeCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCodeCopy : struct, IOpCodeCopy { if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; @@ -42,18 +42,18 @@ public static EvmExceptionType InstructionCodeCopy(IEvm vm, ref Evm public struct OpCallDataCopy : IOpCodeCopy { - public static ReadOnlySpan GetCode(IEvm vm) + public static ReadOnlySpan GetCode(VirtualMachine vm) => vm.State.Env.InputData.Span; } public struct OpCodeCopy : IOpCodeCopy { - public static ReadOnlySpan GetCode(IEvm vm) + public static ReadOnlySpan GetCode(VirtualMachine vm) => vm.State.Env.CodeInfo.MachineCode.Span; } [SkipLocalsInit] - public static EvmExceptionType InstructionExtCodeCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; Address address = stack.PopAddress(); @@ -87,7 +87,7 @@ public static EvmExceptionType InstructionExtCodeCopy(IEvm vm, ref EvmStack stac } [SkipLocalsInit] - public static EvmExceptionType InstructionExtCodeSize(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; gasAvailable -= spec.GetExtCodeCost(); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index e483068bb61..258b100b188 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -22,7 +22,7 @@ public interface IOpCreate } [SkipLocalsInit] - public static EvmExceptionType InstructionCreate(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCreate(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCreate : struct, IOpCreate { Metrics.IncrementCreates(); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index ac198101d88..b43a1a0a4ea 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -19,16 +19,14 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { [SkipLocalsInit] - public static EvmExceptionType InstructionPop(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionPop(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; - stack.PopLimbo(); - - return EvmExceptionType.None; + return stack.PopLimbo() ? EvmExceptionType.None : EvmExceptionType.StackUnderflow; } [SkipLocalsInit] - public static EvmExceptionType InstructionChainId(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionChainId(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (!vm.Spec.ChainIdOpcodeEnabled) return EvmExceptionType.BadInstruction; @@ -39,7 +37,7 @@ public static EvmExceptionType InstructionChainId(IEvm vm, ref EvmStack stack, r } [SkipLocalsInit] - public static EvmExceptionType InstructionBalance(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; gasAvailable -= spec.GetBalanceCost(); @@ -56,7 +54,7 @@ public static EvmExceptionType InstructionBalance(IEvm vm, ref EvmStack stack, r } [SkipLocalsInit] - public static EvmExceptionType InstructionExtCodeHash(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; gasAvailable -= spec.GetExtCodeHashCost(); @@ -89,7 +87,7 @@ public static EvmExceptionType InstructionExtCodeHash(IEvm vm, ref EvmStack stac } [SkipLocalsInit] - public static EvmExceptionType InstructionMLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.VeryLow; @@ -105,7 +103,7 @@ public static EvmExceptionType InstructionMLoad(IEvm vm, ref EvmStack stack, ref } [SkipLocalsInit] - public static EvmExceptionType InstructionMStore(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.VeryLow; @@ -121,7 +119,7 @@ public static EvmExceptionType InstructionMStore(IEvm vm, ref EvmStack stack, re } [SkipLocalsInit] - public static EvmExceptionType InstructionMStore8(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.VeryLow; @@ -137,7 +135,7 @@ public static EvmExceptionType InstructionMStore8(IEvm vm, ref EvmStack stack, r } [SkipLocalsInit] - public static EvmExceptionType InstructionSelfBalance(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.SelfBalance; @@ -147,7 +145,7 @@ public static EvmExceptionType InstructionSelfBalance(IEvm vm, ref EvmStack stac return EvmExceptionType.None; } - public static bool ChargeAccountAccessGas(ref long gasAvailable, IEvm vm, Address address, bool chargeForWarm = true) + public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) { bool result = true; IReleaseSpec spec = vm.Spec; @@ -185,7 +183,7 @@ public interface IOpEnvUInt256 } [SkipLocalsInit] - public static EvmExceptionType InstructionEnvBytes(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionEnvBytes(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEnv : struct, IOpEnvBytes { gasAvailable -= TOpEnv.GasCost; @@ -198,7 +196,7 @@ public static EvmExceptionType InstructionEnvBytes(IEvm vm, ref EvmStack } [SkipLocalsInit] - public static EvmExceptionType InstructionEnvUInt256(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionEnvUInt256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEnv : struct, IOpEnvUInt256 { if (typeof(TOpEnv) == typeof(OpBaseFee) && !vm.Spec.BaseFeeEnabled) return EvmExceptionType.BadInstruction; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index d1df25d4592..58993421f24 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -19,7 +19,7 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { [SkipLocalsInit] - public static EvmExceptionType InstructionReturnDataSize(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionReturnDataSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; @@ -30,7 +30,7 @@ public static EvmExceptionType InstructionReturnDataSize(IEvm vm, ref EvmStack s } [SkipLocalsInit] - public static EvmExceptionType InstructionReturnDataCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; @@ -58,7 +58,7 @@ public static EvmExceptionType InstructionReturnDataCopy(IEvm vm, ref EvmStack s } [SkipLocalsInit] - public static EvmExceptionType InstructionDataLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -74,7 +74,7 @@ public static EvmExceptionType InstructionDataLoad(IEvm vm, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionDataLoadN(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -92,7 +92,7 @@ public static EvmExceptionType InstructionDataLoadN(IEvm vm, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionDataSize(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -106,7 +106,7 @@ public static EvmExceptionType InstructionDataSize(IEvm vm, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionDataCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -137,7 +137,7 @@ public static EvmExceptionType InstructionDataCopy(IEvm vm, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionRelativeJump(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionRelativeJump(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -152,7 +152,7 @@ public static EvmExceptionType InstructionRelativeJump(IEvm vm, ref EvmStack sta } [SkipLocalsInit] - public static EvmExceptionType InstructionRelativeJumpIf(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -172,7 +172,7 @@ public static EvmExceptionType InstructionRelativeJumpIf(IEvm vm, ref EvmStack s } [SkipLocalsInit] - public static EvmExceptionType InstructionJumpTable(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -197,7 +197,7 @@ public static EvmExceptionType InstructionJumpTable(IEvm vm, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionCallFunction(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -231,7 +231,7 @@ public static EvmExceptionType InstructionCallFunction(IEvm vm, ref EvmStack sta } [SkipLocalsInit] - public static EvmExceptionType InstructionReturnFunction(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -249,7 +249,7 @@ public static EvmExceptionType InstructionReturnFunction(IEvm vm, ref EvmStack s } [SkipLocalsInit] - public static EvmExceptionType InstructionJumpFunction(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -271,7 +271,7 @@ public static EvmExceptionType InstructionJumpFunction(IEvm vm, ref EvmStack sta } [SkipLocalsInit] - public static EvmExceptionType InstructionDupN(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -288,7 +288,7 @@ public static EvmExceptionType InstructionDupN(IEvm vm, ref EvmStack stack, ref } [SkipLocalsInit] - public static EvmExceptionType InstructionSwapN(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -305,7 +305,7 @@ public static EvmExceptionType InstructionSwapN(IEvm vm, ref EvmStack stack, ref } [SkipLocalsInit] - public static EvmExceptionType InstructionExchange(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExchange(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) @@ -325,7 +325,7 @@ public static EvmExceptionType InstructionExchange(IEvm vm, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionEofCreate(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.IncrementCreates(); vm.ReturnData = null; @@ -469,7 +469,7 @@ in vm.State.AccessTracker } [SkipLocalsInit] - public static EvmExceptionType InstructionReturnContract(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (!vm.State.ExecutionType.IsAnyCreateEof()) return EvmExceptionType.BadInstruction; @@ -503,7 +503,7 @@ public static EvmExceptionType InstructionReturnContract(IEvm vm, ref EvmStack s } [SkipLocalsInit] - public static EvmExceptionType InstructionReturnDataLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionReturnDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { var spec = vm.Spec; var codeInfo = vm.State.Env.CodeInfo; @@ -543,7 +543,7 @@ public struct OpEofStaticCall : IOpEofCall } [SkipLocalsInit] - public static EvmExceptionType InstructionEofCall(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionEofCall(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEofCall : struct, IOpEofCall { Metrics.IncrementCalls(); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs index b8c912c7dfd..d484800e19f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs @@ -14,7 +14,7 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { [SkipLocalsInit] - public static EvmExceptionType InstructionGas(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionGas(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; @@ -27,7 +27,7 @@ public static EvmExceptionType InstructionGas(IEvm _, ref EvmStack stack, ref lo } [SkipLocalsInit] - public static EvmExceptionType InstructionBlobHash(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionBlobHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; if (!spec.IsEip4844Enabled) return EvmExceptionType.BadInstruction; @@ -51,7 +51,7 @@ public static EvmExceptionType InstructionBlobHash(IEvm vm, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionBlockHash(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.BlockhashOpcode++; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs index cc22ca6fafe..8389feec3e9 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs @@ -11,7 +11,7 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { [SkipLocalsInit] - public static EvmExceptionType InstructionProgramCounter(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionProgramCounter(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; stack.PushUInt32(programCounter - 1); @@ -20,7 +20,7 @@ public static EvmExceptionType InstructionProgramCounter(IEvm _, ref EvmStack st } [SkipLocalsInit] - public static EvmExceptionType InstructionJumpDest(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionJumpDest(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.JumpDest; @@ -28,7 +28,7 @@ public static EvmExceptionType InstructionJumpDest(IEvm _, ref EvmStack stack, r } [SkipLocalsInit] - public static EvmExceptionType InstructionJump(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionJump(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Mid; if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; @@ -39,7 +39,7 @@ public static EvmExceptionType InstructionJump(IEvm vm, ref EvmStack stack, ref [SkipLocalsInit] [MethodImpl(MethodImplOptions.NoInlining)] - public static EvmExceptionType InstructionJumpIf(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionJumpIf(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.High; if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs index d69c826aa3e..de5d49e3ff1 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs @@ -16,7 +16,7 @@ public interface IOpMath1Param } [SkipLocalsInit] - public static EvmExceptionType InstructionMath1Param(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMath1Param(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpMath : struct, IOpMath1Param { gasAvailable -= TOpMath.GasCost; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs index 80af332adde..8ac520c8088 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs @@ -17,7 +17,7 @@ public interface IOpMath2Param } [SkipLocalsInit] - public static EvmExceptionType InstructionMath2Param(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMath2Param(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpMath : struct, IOpMath2Param { gasAvailable -= TOpMath.GasCost; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs index 2366a4b87ad..e5ac578de73 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs @@ -15,7 +15,7 @@ public interface IOpMath3Param } [SkipLocalsInit] - public static EvmExceptionType InstructionMath3Param(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMath3Param(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpMath : struct, IOpMath3Param { gasAvailable -= TOpMath.GasCost; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs index 8c17fb20931..4fa5216bdb6 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs @@ -16,7 +16,7 @@ public interface IOpShift } [SkipLocalsInit] - public static EvmExceptionType InstructionShift(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionShift(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpShift : struct, IOpShift { gasAvailable -= TOpShift.GasCost; @@ -24,7 +24,7 @@ public static EvmExceptionType InstructionShift(IEvm vm, ref EvmStack if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; if (a >= 256) { - stack.PopLimbo(); + if (!stack.PopLimbo()) return EvmExceptionType.StackUnderflow; stack.PushZero(); } else @@ -38,7 +38,7 @@ public static EvmExceptionType InstructionShift(IEvm vm, ref EvmStack } [SkipLocalsInit] - public static EvmExceptionType InstructionSar(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionSar(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.VeryLow; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index 5bd24cd09e8..7629df55cc8 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -51,7 +51,7 @@ public struct Op31 : IOpCount { public static int Count => 31; } public struct Op32 : IOpCount { public static int Count => 32; } [SkipLocalsInit] - public static EvmExceptionType InstructionDup(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionDup(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCount : IOpCount { gasAvailable -= GasCostOf.VeryLow; @@ -61,7 +61,7 @@ public static EvmExceptionType InstructionDup(IEvm _, ref EvmStack sta } [SkipLocalsInit] - public static EvmExceptionType InstructionSwap(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionSwap(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCount : IOpCount { gasAvailable -= GasCostOf.VeryLow; @@ -71,7 +71,7 @@ public static EvmExceptionType InstructionSwap(IEvm _, ref EvmStack st } [SkipLocalsInit] - public static EvmExceptionType InstructionPush0(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionPush0(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; @@ -81,7 +81,7 @@ public static EvmExceptionType InstructionPush0(IEvm vm, ref EvmStack stack, ref } [SkipLocalsInit] - public static EvmExceptionType InstructionPush(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionPush(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCount : IOpCount { gasAvailable -= GasCostOf.VeryLow; @@ -98,7 +98,7 @@ public static EvmExceptionType InstructionPush(IEvm vm, ref EvmStack s } [SkipLocalsInit] - public static EvmExceptionType InstructionLog(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionLog(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCount : struct, IOpCount { EvmState vmState = vm.State; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index de06eae4143..8624956f468 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -22,7 +22,7 @@ internal enum StorageAccessType } [SkipLocalsInit] - public static EvmExceptionType InstructionTLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionTLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.TloadOpcode++; gasAvailable -= GasCostOf.TLoad; @@ -43,7 +43,7 @@ public static EvmExceptionType InstructionTLoad(IEvm vm, ref EvmStack stack, ref } [SkipLocalsInit] - public static EvmExceptionType InstructionTStore(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.TstoreOpcode++; EvmState vmState = vm.State; @@ -67,7 +67,7 @@ public static EvmExceptionType InstructionTStore(IEvm vm, ref EvmStack stack, re } [SkipLocalsInit] - public static EvmExceptionType InstructionMCopy(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.MCopyOpcode++; @@ -93,7 +93,7 @@ public static EvmExceptionType InstructionMCopy(IEvm vm, ref EvmStack stack, ref [SkipLocalsInit] [MethodImpl(MethodImplOptions.NoInlining)] - internal static EvmExceptionType InstructionSStore(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.IncrementSStoreOpcode(); EvmState vmState = vm.State; @@ -237,7 +237,7 @@ internal static EvmExceptionType InstructionSStore(IEvm vm, ref EvmStack stack, [SkipLocalsInit] [MethodImpl(MethodImplOptions.NoInlining)] - internal static EvmExceptionType InstructionSLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; Metrics.IncrementSLoadOpcode(); @@ -264,7 +264,7 @@ internal static EvmExceptionType InstructionSLoad(IEvm vm, ref EvmStack stack, r internal static bool ChargeStorageAccessGas( ref long gasAvailable, - IEvm vm, + VirtualMachine vm, in StorageCell storageCell, StorageAccessType storageAccessType, IReleaseSpec spec) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index f8d8c685aae..cbca84925ed 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -10,14 +10,14 @@ using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm; -using unsafe OpCode = delegate*; +using unsafe OpCode = delegate*; using Int256; internal unsafe sealed partial class EvmInstructions { public static OpCode[] GenerateOpCodes(IReleaseSpec spec) { - var lookup = new delegate*[256]; + var lookup = new delegate*[256]; for (int i = 0; i < lookup.Length; i++) { @@ -264,7 +264,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) } [SkipLocalsInit] - public static EvmExceptionType InstructionStop(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionStop(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (vm.State.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) { @@ -275,7 +275,7 @@ public static EvmExceptionType InstructionStop(IEvm vm, ref EvmStack stack, ref } [SkipLocalsInit] - public static EvmExceptionType InstructionRevert(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionRevert(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (!stack.PopUInt256(out UInt256 position) || !stack.PopUInt256(out UInt256 length)) @@ -292,7 +292,7 @@ public static EvmExceptionType InstructionRevert(IEvm vm, ref EvmStack stack, re } [SkipLocalsInit] - private static EvmExceptionType InstructionSelfDestruct(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.IncrementSelfDestructs(); @@ -346,7 +346,7 @@ private static EvmExceptionType InstructionSelfDestruct(IEvm vm, ref EvmStack st } [SkipLocalsInit] - public static EvmExceptionType InstructionPrevRandao(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionPrevRandao(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; BlockHeader header = vm.State.Env.TxExecutionContext.BlockExecutionContext.Header; @@ -363,17 +363,17 @@ public static EvmExceptionType InstructionPrevRandao(IEvm vm, ref EvmStack stack return EvmExceptionType.None; } - public static EvmExceptionType InstructionInvalid(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionInvalid(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.High; return EvmExceptionType.BadInstruction; } - public static EvmExceptionType InstructionBadInstruction(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionBadInstruction(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) => EvmExceptionType.BadInstruction; [SkipLocalsInit] - public static EvmExceptionType InstructionExp(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExp(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Exp; @@ -409,7 +409,7 @@ public static EvmExceptionType InstructionExp(IEvm vm, ref EvmStack stack, ref l } [SkipLocalsInit] - public static EvmExceptionType InstructionByte(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionByte(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.VeryLow; @@ -437,7 +437,7 @@ public static EvmExceptionType InstructionByte(IEvm _, ref EvmStack stack, ref l } [SkipLocalsInit] - public static EvmExceptionType InstructionSignExtend(IEvm _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionSignExtend(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Low; @@ -467,7 +467,7 @@ public static EvmExceptionType InstructionSignExtend(IEvm _, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionKeccak256(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionKeccak256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; @@ -483,7 +483,7 @@ public static EvmExceptionType InstructionKeccak256(IEvm vm, ref EvmStack stack, } [SkipLocalsInit] - public static EvmExceptionType InstructionCallDataLoad(IEvm vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCallDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.VeryLow; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 2975f192113..9a9ca7de195 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -18,9 +18,6 @@ using Nethermind.Evm.Tracing; using Nethermind.Logging; using Nethermind.State; -using static Nethermind.Evm.VirtualMachine; -using static System.Runtime.CompilerServices.Unsafe; -using static Nethermind.Evm.EvmInstructions; using static Nethermind.Evm.EvmObjectFormat.EofValidator; #if DEBUG @@ -31,14 +28,14 @@ namespace Nethermind.Evm; -using unsafe OpCode = delegate*; +using unsafe OpCode = delegate*; using Int256; using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.EvmObjectFormat.Handlers; using System.Runtime.InteropServices; -public unsafe class VirtualMachine : IEvm, IVirtualMachine +public sealed unsafe class VirtualMachine : IVirtualMachine { public const int MaxCallDepth = Eof1.RETURN_STACK_MAX_HEIGHT; private readonly static UInt256 P255Int = (UInt256)System.Numerics.BigInteger.Pow(2, 255); @@ -169,10 +166,10 @@ public interface IIsTracing { } public ITxTracer TxTracer => _txTracer; public IWorldState WorldState => _state; public ReadOnlySpan ChainId => _chainId; - ReadOnlyMemory IEvm.ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } - object IEvm.ReturnData { get => _returnData; set => _returnData = value; } + ReadOnlyMemory ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } + object ReturnData { get => _returnData; set => _returnData = value; } object _returnData; - IBlockhashProvider IEvm.BlockhashProvider => _blockhashProvider; + IBlockhashProvider BlockhashProvider => _blockhashProvider; OpCode[] _opcodeMethods; @@ -782,9 +779,9 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousC return CallResult.OutOfGasException; } - EvmState IEvm.State => _vmState; + EvmState State => _vmState; EvmState _vmState; - int IEvm.SectionIndex { get => _sectionIndex; set => _sectionIndex = value; } + int SectionIndex { get => _sectionIndex; set => _sectionIndex = value; } int _sectionIndex; [SkipLocalsInit] From a9949bf7c251f72fb69e09acc21d21d46dfd5cdf Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 31 Jan 2025 19:16:34 +0000 Subject: [PATCH 177/255] Fixes --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 9a9ca7de195..3bd54fc23f0 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -166,10 +166,15 @@ public interface IIsTracing { } public ITxTracer TxTracer => _txTracer; public IWorldState WorldState => _state; public ReadOnlySpan ChainId => _chainId; - ReadOnlyMemory ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } - object ReturnData { get => _returnData; set => _returnData = value; } + public ReadOnlyMemory ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } + public object ReturnData { get => _returnData; set => _returnData = value; } object _returnData; - IBlockhashProvider BlockhashProvider => _blockhashProvider; + public IBlockhashProvider BlockhashProvider => _blockhashProvider; + + public EvmState State => _vmState; + EvmState _vmState; + public int SectionIndex { get => _sectionIndex; set => _sectionIndex = value; } + int _sectionIndex; OpCode[] _opcodeMethods; @@ -779,11 +784,6 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousC return CallResult.OutOfGasException; } - EvmState State => _vmState; - EvmState _vmState; - int SectionIndex { get => _sectionIndex; set => _sectionIndex = value; } - int _sectionIndex; - [SkipLocalsInit] private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailable) { From 5c795bda8eef069b5de627802cc0d116c37b1fd6 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 31 Jan 2025 19:30:27 +0000 Subject: [PATCH 178/255] Clean up --- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 2 +- .../EvmObjectFormat/Handlers/EofV1.cs | 8 +- .../Instructions/EvmInstructions.Call.cs | 2 +- .../Instructions/EvmInstructions.CodeCopy.cs | 2 +- .../Instructions/EvmInstructions.Create.cs | 3 +- .../Instructions/EvmInstructions.Eof.cs | 76 +++++++++---------- .../Instructions/EvmInstructions.Extras.cs | 2 +- .../Instructions/EvmInstructions.cs | 11 +-- .../GethStyle/GethLikeBlockFileTracer.cs | 2 +- .../Tracing/GethStyle/GethLikeTxFileTracer.cs | 6 +- .../GethStyle/GethLikeTxMemoryTracer.cs | 2 +- 11 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index 0facb32c3b4..1d0ed1fc073 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -34,7 +34,7 @@ public static byte[] CreateCodeBitmap(ReadOnlySpan code, bool isEof = fals for (int pc = 0; pc < code.Length;) { - var opMetadaata = ((Instruction)code[pc]).StackRequirements(); + (ushort? InputCount, ushort? OutputCount, ushort? immediates) opMetadaata = ((Instruction)code[pc]).StackRequirements(); pc++; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index e2efd6b8422..dde44a1078e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -405,7 +405,7 @@ private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy val { Queue<(EofContainer container, ValidationStrategy strategy)> containers = new(); containers.Enqueue((eofContainer, validationStrategy)); - while (containers.TryDequeue(out var target)) + while (containers.TryDequeue(out (EofContainer container, ValidationStrategy strategy) target)) { EofContainer targetContainer = target.container; validationStrategy = target.strategy; @@ -415,7 +415,7 @@ private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy val containerQueue.VisitedContainers[0] = GetValidation(validationStrategy); - while (containerQueue.TryDequeue(out var worklet)) + while (containerQueue.TryDequeue(out (int Index, ValidationStrategy Strategy) worklet)) { if (worklet.Index != 0) { @@ -497,7 +497,7 @@ private bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida ReadOnlySpan contractBody = container[startOffset..endOffset]; ReadOnlySpan dataBody = container[endOffset..]; - var typeSection = header.TypeSection; + SectionHeader typeSection = header.TypeSection; (int typeSectionStart, int typeSectionSize) = (typeSection.Start, typeSection.Size); if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS + 1) @@ -553,7 +553,7 @@ private bool ValidateCodeSections(EofContainer eofContainer, ValidationStrategy sectionQueue.Enqueue(0, strategy); - while (sectionQueue.TryDequeue(out var sectionIdx)) + while (sectionQueue.TryDequeue(out (int Index, ValidationStrategy Strategy) sectionIdx)) { if (sectionQueue.VisitedContainers[sectionIdx.Index] != 0) continue; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index c995f2357f7..94e9df0c636 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -27,7 +27,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E { Metrics.IncrementCalls(); - var spec = vm.Spec; + IReleaseSpec spec = vm.Spec; vm.ReturnData = null; ref readonly ExecutionEnvironment env = ref vm.State.Env; IWorldState state = vm.WorldState; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 78913f89085..122db0266bc 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -97,7 +97,7 @@ public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref Evm if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; - var codeSection = vm.State.Env.CodeInfo.MachineCode.Span; + ReadOnlySpan codeSection = vm.State.Env.CodeInfo.MachineCode.Span; if (!vm.TxTracer.IsTracingInstructions && programCounter < codeSection.Length) { bool optimizeAccess = false; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 258b100b188..d69ae51579f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using Nethermind.Core; +using Nethermind.Core.Specs; using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.EvmObjectFormat; using Nethermind.Int256; @@ -27,7 +28,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r { Metrics.IncrementCreates(); - var spec = vm.Spec; + IReleaseSpec spec = vm.Spec; if (vm.State.IsStatic) return EvmExceptionType.StaticCallViolation; vm.ReturnData = null; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 58993421f24..bce72a20d92 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -5,15 +5,15 @@ 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.State; namespace Nethermind.Evm; using Int256; -using Nethermind.Evm.EvmObjectFormat; -using Nethermind.Evm.EvmObjectFormat.Handlers; - using static Nethermind.Evm.VirtualMachine; internal sealed partial class EvmInstructions @@ -60,13 +60,13 @@ public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref [SkipLocalsInit] public static EvmExceptionType InstructionDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; if (!UpdateGas(GasCostOf.DataLoad, ref gasAvailable)) return EvmExceptionType.OutOfGas; - stack.PopUInt256(out var a); + stack.PopUInt256(out UInt256 a); ZeroPaddedSpan zpbytes = codeInfo.DataSection.SliceWithZeroPadding(a, 32); stack.PushBytes(zpbytes); @@ -76,7 +76,7 @@ public static EvmExceptionType InstructionDataLoad(VirtualMachine vm, ref EvmSta [SkipLocalsInit] public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -94,7 +94,7 @@ public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmSt [SkipLocalsInit] public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -108,7 +108,7 @@ public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmSta [SkipLocalsInit] public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -139,7 +139,7 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta [SkipLocalsInit] public static EvmExceptionType InstructionRelativeJump(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -154,7 +154,7 @@ public static EvmExceptionType InstructionRelativeJump(VirtualMachine vm, ref Ev [SkipLocalsInit] public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -174,14 +174,14 @@ public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref [SkipLocalsInit] public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) return EvmExceptionType.OutOfGas; - stack.PopUInt256(out var a); - var codeSection = codeInfo.CodeSection.Span; + stack.PopUInt256(out UInt256 a); + ReadOnlySpan codeSection = codeInfo.CodeSection.Span; var count = codeSection[programCounter] + 1; var immediates = (ushort)(count * EofValidator.TWO_BYTE_LENGTH + EofValidator.ONE_BYTE_LENGTH); @@ -199,13 +199,13 @@ public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmSt [SkipLocalsInit] public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; if (!UpdateGas(GasCostOf.Callf, ref gasAvailable)) return EvmExceptionType.OutOfGas; - var codeSection = codeInfo.CodeSection.Span; + ReadOnlySpan codeSection = codeInfo.CodeSection.Span; var index = (int)codeSection.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); (int inputCount, _, int maxStackHeight) = codeInfo.GetSectionMetadata(index); @@ -233,7 +233,7 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev [SkipLocalsInit] public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -241,7 +241,7 @@ public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref (_, int outputCount, _) = codeInfo.GetSectionMetadata(vm.SectionIndex); - var stackFrame = vm.State.ReturnStack[--vm.State.ReturnStackHead]; + EvmState.ReturnState stackFrame = vm.State.ReturnStack[--vm.State.ReturnStackHead]; vm.SectionIndex = stackFrame.Index; programCounter = stackFrame.Offset; @@ -251,13 +251,13 @@ public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref [SkipLocalsInit] public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; if (!UpdateGas(GasCostOf.Jumpf, ref gasAvailable)) return EvmExceptionType.OutOfGas; - var index = (int)codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + int index = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); (int inputCount, _, int maxStackHeight) = codeInfo.GetSectionMetadata(index); if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) @@ -273,13 +273,13 @@ public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref Ev [SkipLocalsInit] public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) return EvmExceptionType.OutOfGas; - int imm = (int)codeInfo.CodeSection.Span[programCounter]; + int imm = codeInfo.CodeSection.Span[programCounter]; stack.Dup(imm + 1); programCounter += 1; @@ -290,7 +290,7 @@ public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack s [SkipLocalsInit] public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -307,13 +307,13 @@ public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack [SkipLocalsInit] public static EvmExceptionType InstructionExchange(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) return EvmExceptionType.OutOfGas; - var codeSection = codeInfo.CodeSection.Span; + ReadOnlySpan codeSection = codeInfo.CodeSection.Span; int n = 1 + (int)(codeSection[programCounter] >> 0x04); int m = 1 + (int)(codeSection[programCounter] & 0x0f); @@ -330,8 +330,8 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt Metrics.IncrementCreates(); vm.ReturnData = null; - var spec = vm.Spec; - var codeInfo = vm.State.Env.CodeInfo; + IReleaseSpec spec = vm.Spec; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -339,13 +339,13 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt ref readonly ExecutionEnvironment env = ref vm.State.Env; EofCodeInfo container = env.CodeInfo as EofCodeInfo; - var currentContext = ExecutionType.EOFCREATE; + ExecutionType currentContext = ExecutionType.EOFCREATE; // 1 - deduct TX_CREATE_COST gas if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) return EvmExceptionType.OutOfGas; - var codeSection = codeInfo.CodeSection.Span; + ReadOnlySpan codeSection = codeInfo.CodeSection.Span; // 2 - read immediate operand initcontainer_index, encoded as 8-bit unsigned value int initcontainerIndex = codeSection[programCounter++]; @@ -376,7 +376,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt if (!UpdateGas(hashCost, ref gasAvailable)) return EvmExceptionType.OutOfGas; - var state = vm.WorldState; + IWorldState state = vm.WorldState; // 7 - check that current call depth is below STACK_DEPTH_LIMIT and that caller balance is enough to transfer value // in case of failure return 0 on the stack, caller’s nonce is not updated and gas for initcode execution is not consumed. UInt256 balance = state.GetBalance(env.ExecutingAccount); @@ -477,15 +477,15 @@ public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref if (!UpdateGas(GasCostOf.ReturnContract, ref gasAvailable)) return EvmExceptionType.OutOfGas; - var spec = vm.Spec; - var codeInfo = vm.State.Env.CodeInfo; + IReleaseSpec spec = vm.Spec; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; byte sectionIdx = codeInfo.CodeSection.Span[programCounter++]; ReadOnlyMemory deployCode = codeInfo.ContainerSection[(Range)codeInfo.ContainerSectionOffset(sectionIdx)]; EofCodeInfo deploycodeInfo = (EofCodeInfo)CodeInfoFactory.CreateCodeInfo(deployCode, spec, EvmObjectFormat.ValidationStrategy.ExractHeader); - stack.PopUInt256(out var a); - stack.PopUInt256(out var b); + stack.PopUInt256(out UInt256 a); + stack.PopUInt256(out UInt256 b); ReadOnlyMemory auxData = ReadOnlyMemory.Empty; if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; @@ -505,16 +505,16 @@ public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref [SkipLocalsInit] public static EvmExceptionType InstructionReturnDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - var spec = vm.Spec; - var codeInfo = vm.State.Env.CodeInfo; + IReleaseSpec spec = vm.Spec; + ICodeInfo codeInfo = vm.State.Env.CodeInfo; if (!spec.IsEofEnabled || codeInfo.Version == 0) return EvmExceptionType.BadInstruction; gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - var slice = vm.ReturnDataBuffer.Span.SliceWithZeroPadding(a, 32); + ZeroPaddedSpan slice = vm.ReturnDataBuffer.Span.SliceWithZeroPadding(a, 32); stack.PushBytes(slice); return EvmExceptionType.None; @@ -550,7 +550,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, const int MIN_RETAINED_GAS = 5000; - var spec = vm.Spec; + IReleaseSpec spec = vm.Spec; vm.ReturnData = null; ref readonly ExecutionEnvironment env = ref vm.State.Env; IWorldState state = vm.WorldState; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs index d484800e19f..662e72d2bd5 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs @@ -57,7 +57,7 @@ public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmSt gasAvailable -= GasCostOf.BlockHash; - if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; long number = a > long.MaxValue ? long.MaxValue : (long)a; Hash256? blockHash = vm.BlockhashProvider.GetBlockhash(vm.State.Env.TxExecutionContext.BlockExecutionContext.Header, number); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index cbca84925ed..cb3f50d7b09 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -7,6 +7,7 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; +using Nethermind.State; using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm; @@ -297,8 +298,8 @@ private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref E Metrics.IncrementSelfDestructs(); EvmState vmState = vm.State; - var spec = vm.Spec; - var state = vm.WorldState; + IReleaseSpec spec = vm.Spec; + IWorldState state = vm.WorldState; if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; @@ -377,7 +378,7 @@ public static EvmExceptionType InstructionExp(VirtualMachine vm, ref EvmStack st { gasAvailable -= GasCostOf.Exp; - if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; Span bytes = stack.PopWord256(); int leadingZeros = bytes.LeadingZerosCount(); @@ -413,7 +414,7 @@ public static EvmExceptionType InstructionByte(VirtualMachine _, ref EvmStack st { gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; Span bytes = stack.PopWord256(); if (a >= BigInt32) @@ -441,7 +442,7 @@ public static EvmExceptionType InstructionSignExtend(VirtualMachine _, ref EvmSt { gasAvailable -= GasCostOf.Low; - if (!stack.PopUInt256(out var a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; if (a >= BigInt32) { if (!stack.EnsureDepth(1)) return EvmExceptionType.StackUnderflow; diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockFileTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockFileTracer.cs index 1267e39ffc8..15d0166e0c0 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockFileTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockFileTracer.cs @@ -52,7 +52,7 @@ protected override void AddTrace(GethLikeTxTrace trace) { } protected override GethLikeTxTrace OnEnd(GethLikeTxFileTracer txTracer) { - var trace = txTracer.BuildResult(); + GethLikeTxTrace trace = txTracer.BuildResult(); JsonSerializer.Serialize(_jsonWriter, new diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxFileTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxFileTracer.cs index 9d3d8ed6957..99c383f5dd2 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxFileTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxFileTracer.cs @@ -21,7 +21,7 @@ public GethLikeTxFileTracer(Action dumpCallback, GethTrace public override GethLikeTxTrace BuildResult() { - var trace = base.BuildResult(); + GethLikeTxTrace trace = base.BuildResult(); if (_startGas.HasValue) trace.Gas = _startGas.Value - CurrentTraceEntry.Gas; @@ -40,7 +40,7 @@ protected override void AddTraceEntry(GethTxFileTraceEntry entry) protected override GethTxFileTraceEntry CreateTraceEntry(Instruction opcode) { - var entry = GetOrCreateTraceEntry(); + GethTxFileTraceEntry entry = GetOrCreateTraceEntry(); entry.OpcodeRaw = opcode; @@ -52,7 +52,7 @@ private GethTxFileTraceEntry GetOrCreateTraceEntry() if (CurrentTraceEntry is null) return new(); - var entry = CurrentTraceEntry; + GethTxFileTraceEntry entry = CurrentTraceEntry; entry.Depth = default; entry.Error = default; diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs index d7f7315aebd..74583a3cac6 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs @@ -37,7 +37,7 @@ public override void SetOperationStorage(Address address, UInt256 storageIndex, public override void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { - var previousTraceEntry = CurrentTraceEntry; + GethTxMemoryTraceEntry previousTraceEntry = CurrentTraceEntry; var previousDepth = CurrentTraceEntry?.Depth ?? 0; base.StartOperation(pc, opcode, gas, env, codeSection, functionDepth); From dd36155b6ba589154a591ea8cc47a8d6d74f54f0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 31 Jan 2025 19:54:46 +0000 Subject: [PATCH 179/255] Tidy up --- .../Instructions/EvmInstructions.Call.cs | 20 +- .../Instructions/EvmInstructions.CodeCopy.cs | 14 +- .../Instructions/EvmInstructions.Create.cs | 14 +- .../EvmInstructions.Environment.cs | 14 +- .../Instructions/EvmInstructions.Eof.cs | 80 +++--- .../Instructions/EvmInstructions.Extras.cs | 4 +- .../Instructions/EvmInstructions.Jump.cs | 4 +- .../Instructions/EvmInstructions.Stack.cs | 4 +- .../Instructions/EvmInstructions.Storage.cs | 12 +- .../Instructions/EvmInstructions.cs | 14 +- .../Nethermind.Evm/VirtualMachine.cs | 235 +----------------- 11 files changed, 103 insertions(+), 312 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 94e9df0c636..f5b8f589c60 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -29,7 +29,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E IReleaseSpec spec = vm.Spec; vm.ReturnData = null; - ref readonly ExecutionEnvironment env = ref vm.State.Env; + ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; IWorldState state = vm.WorldState; if (!stack.PopUInt256(out UInt256 gasLimit)) return EvmExceptionType.StackUnderflow; @@ -58,7 +58,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E if (!stack.PopUInt256(out UInt256 outputOffset)) return EvmExceptionType.StackUnderflow; if (!stack.PopUInt256(out UInt256 outputLength)) return EvmExceptionType.StackUnderflow; - if (vm.State.IsStatic && !transferValue.IsZero && typeof(TOpCall) == typeof(OpCallCode)) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic && !transferValue.IsZero && typeof(TOpCall) == typeof(OpCallCode)) return EvmExceptionType.StaticCallViolation; Address caller = typeof(TOpCall) == typeof(OpDelegateCall) ? env.Caller : env.ExecutingAccount; Address target = typeof(TOpCall) == typeof(OpCall) || typeof(TOpCall) == typeof(OpStaticCall) @@ -87,8 +87,8 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E } if (!UpdateGas(spec.GetCallCost(), ref gasAvailable) || - !UpdateMemoryCost(vm.State, ref gasAvailable, in dataOffset, dataLength) || - !UpdateMemoryCost(vm.State, ref gasAvailable, in outputOffset, outputLength) || + !UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataLength) || + !UpdateMemoryCost(vm.EvmState, ref gasAvailable, in outputOffset, outputLength) || !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; ICodeInfo codeInfo = vm.CodeInfoRepository.GetCachedCodeInfo(state, codeSource, spec); @@ -144,7 +144,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E return FastCall(vm, spec, in transferValue, target); } - ReadOnlyMemory callData = vm.State.Memory.Load(in dataOffset, dataLength); + ReadOnlyMemory callData = vm.EvmState.Memory.Load(in dataOffset, dataLength); ExecutionEnvironment callEnv = new ( txExecutionContext: in env.TxExecutionContext, @@ -169,11 +169,11 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E outputOffset.ToLong(), outputLength.ToLong(), TOpCall.ExecutionType, - TOpCall.IsStatic || vm.State.IsStatic, + TOpCall.IsStatic || vm.EvmState.IsStatic, isCreateOnPreExistingAccount: false, snapshot: snapshot, env: callEnv, - stateForAccessLists: vm.State.AccessTracker); + stateForAccessLists: vm.EvmState.AccessTracker); return EvmExceptionType.None; @@ -229,7 +229,7 @@ public struct OpStaticCall : IOpCall [SkipLocalsInit] public static EvmExceptionType InstructionReturn(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - if (vm.State.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) + if (vm.EvmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) { return EvmExceptionType.BadInstruction; } @@ -238,12 +238,12 @@ public static EvmExceptionType InstructionReturn(VirtualMachine vm, ref EvmStack !stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in position, in length)) + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in position, in length)) { return EvmExceptionType.OutOfGas; } - vm.ReturnData = vm.State.Memory.Load(in position, in length).ToArray(); + vm.ReturnData = vm.EvmState.Memory.Load(in position, in length).ToArray(); return EvmExceptionType.None; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 122db0266bc..1b1a5a529aa 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -28,9 +28,9 @@ public static EvmExceptionType InstructionCodeCopy(VirtualMachine v if (!result.IsZero) { - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, result)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) return EvmExceptionType.OutOfGas; ZeroPaddedSpan slice = TOpCodeCopy.GetCode(vm).SliceWithZeroPadding(in b, (int)result); - vm.State.Memory.Save(in a, in slice); + vm.EvmState.Memory.Save(in a, in slice); if (vm.TxTracer.IsTracingInstructions) { vm.TxTracer.ReportMemoryChange(a, in slice); @@ -43,13 +43,13 @@ public static EvmExceptionType InstructionCodeCopy(VirtualMachine v public struct OpCallDataCopy : IOpCodeCopy { public static ReadOnlySpan GetCode(VirtualMachine vm) - => vm.State.Env.InputData.Span; + => vm.EvmState.Env.InputData.Span; } public struct OpCodeCopy : IOpCodeCopy { public static ReadOnlySpan GetCode(VirtualMachine vm) - => vm.State.Env.CodeInfo.MachineCode.Span; + => vm.EvmState.Env.CodeInfo.MachineCode.Span; } [SkipLocalsInit] @@ -68,7 +68,7 @@ public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref Evm if (!result.IsZero) { - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, result)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) return EvmExceptionType.OutOfGas; ReadOnlySpan externalCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, spec).MachineCode.Span; if (spec.IsEofEnabled && EofValidator.IsEof(externalCode, out _)) @@ -76,7 +76,7 @@ public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref Evm externalCode = EofValidator.MAGIC; } ZeroPaddedSpan slice = externalCode.SliceWithZeroPadding(in b, (int)result); - vm.State.Memory.Save(in a, in slice); + vm.EvmState.Memory.Save(in a, in slice); if (vm.TxTracer.IsTracingInstructions) { vm.TxTracer.ReportMemoryChange(a, in slice); @@ -97,7 +97,7 @@ public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref Evm if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; - ReadOnlySpan codeSection = vm.State.Env.CodeInfo.MachineCode.Span; + ReadOnlySpan codeSection = vm.EvmState.Env.CodeInfo.MachineCode.Span; if (!vm.TxTracer.IsTracingInstructions && programCounter < codeSection.Length) { bool optimizeAccess = false; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index d69ae51579f..59f1d7c7105 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -29,10 +29,10 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r Metrics.IncrementCreates(); IReleaseSpec spec = vm.Spec; - if (vm.State.IsStatic) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic) return EvmExceptionType.StaticCallViolation; vm.ReturnData = null; - ref readonly ExecutionEnvironment env = ref vm.State.Env; + ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; IWorldState state = vm.WorldState; // TODO: happens in CREATE_empty000CreateInitCode_Transaction but probably has to be handled differently @@ -66,7 +66,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r if (!UpdateGas(gasCost, ref gasAvailable)) return EvmExceptionType.OutOfGas; - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in memoryPositionOfInitCode, in initCodeLength)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memoryPositionOfInitCode, in initCodeLength)) return EvmExceptionType.OutOfGas; // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients @@ -77,7 +77,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r return EvmExceptionType.None; } - ReadOnlyMemory initCode = vm.State.Memory.Load(in memoryPositionOfInitCode, in initCodeLength); + ReadOnlyMemory initCode = vm.EvmState.Memory.Load(in memoryPositionOfInitCode, in initCodeLength); UInt256 balance = state.GetBalance(env.ExecutingAccount); if (value > balance) @@ -109,7 +109,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r if (spec.UseHotAndColdStorage) { // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 - vm.State.AccessTracker.WarmUp(contractAddress); + vm.EvmState.AccessTracker.WarmUp(contractAddress); } // Do not add the initCode to the cache as it is @@ -165,11 +165,11 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r outputDestination: 0, outputLength: 0, TOpCreate.ExecutionType, - isStatic: vm.State.IsStatic, + isStatic: vm.EvmState.IsStatic, isCreateOnPreExistingAccount: accountExists, in snapshot, env: in callEnv, - in vm.State.AccessTracker + in vm.EvmState.AccessTracker ); return EvmExceptionType.None; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index b43a1a0a4ea..52661832d3b 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -92,7 +92,7 @@ public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack gasAvailable -= GasCostOf.VeryLow; if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) return EvmExceptionType.OutOfGas; Span bytes = vmState.Memory.LoadSpan(in result); if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange(result, bytes); @@ -110,7 +110,7 @@ public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; Span bytes = stack.PopWord256(); - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) return EvmExceptionType.OutOfGas; vmState.Memory.SaveWord(in result, bytes); if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange((long)result, bytes); @@ -126,7 +126,7 @@ public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStac if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; byte data = stack.PopByte(); - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in UInt256.One)) return EvmExceptionType.OutOfGas; vmState.Memory.SaveByte(in result, data); if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange(result, data); @@ -139,7 +139,7 @@ public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref Evm { gasAvailable -= GasCostOf.SelfBalance; - UInt256 result = vm.WorldState.GetBalance(vm.State.Env.ExecutingAccount); + UInt256 result = vm.WorldState.GetBalance(vm.EvmState.Env.ExecutingAccount); stack.PushUInt256(in result); return EvmExceptionType.None; @@ -151,7 +151,7 @@ public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine IReleaseSpec spec = vm.Spec; if (spec.UseHotAndColdStorage) { - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (vm.TxTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list { vmState.AccessTracker.WarmUp(address); @@ -188,7 +188,7 @@ public static EvmExceptionType InstructionEnvBytes(VirtualMachine vm, re { gasAvailable -= TOpEnv.GasCost; - TOpEnv.Operation(vm.State, out Span result); + TOpEnv.Operation(vm.EvmState, out Span result); stack.PushBytes(result); @@ -203,7 +203,7 @@ public static EvmExceptionType InstructionEnvUInt256(VirtualMachine vm, if (typeof(TOpEnv) == typeof(OpBlobBaseFee) && !vm.Spec.BlobBaseFeeEnabled) return EvmExceptionType.BadInstruction; gasAvailable -= TOpEnv.GasCost; - TOpEnv.Operation(vm.State, out UInt256 result); + TOpEnv.Operation(vm.EvmState, out UInt256 result); stack.PushUInt256(in result); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index bce72a20d92..250f66e6e94 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -38,16 +38,16 @@ public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in c); ReadOnlyMemory returnDataBuffer = vm.ReturnDataBuffer; - if (vm.State.Env.CodeInfo.Version == 0 && (UInt256.AddOverflow(c, b, out UInt256 result) || result > returnDataBuffer.Length)) + if (vm.EvmState.Env.CodeInfo.Version == 0 && (UInt256.AddOverflow(c, b, out UInt256 result) || result > returnDataBuffer.Length)) { return EvmExceptionType.AccessViolation; } if (!c.IsZero) { - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, c)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, c)) return EvmExceptionType.OutOfGas; ZeroPaddedSpan slice = returnDataBuffer.Span.SliceWithZeroPadding(b, (int)c); - vm.State.Memory.Save(in a, in slice); + vm.EvmState.Memory.Save(in a, in slice); if (vm.TxTracer.IsTracingInstructions) { vm.TxTracer.ReportMemoryChange(a, in slice); @@ -60,7 +60,7 @@ public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref [SkipLocalsInit] public static EvmExceptionType InstructionDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -76,7 +76,7 @@ public static EvmExceptionType InstructionDataLoad(VirtualMachine vm, ref EvmSta [SkipLocalsInit] public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -94,7 +94,7 @@ public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmSt [SkipLocalsInit] public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -108,7 +108,7 @@ public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmSta [SkipLocalsInit] public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -121,10 +121,10 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta if (!size.IsZero) { - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in memOffset, size)) + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memOffset, size)) return EvmExceptionType.OutOfGas; ZeroPaddedSpan dataSectionSlice = codeInfo.DataSection.SliceWithZeroPadding(offset, (int)size); - vm.State.Memory.Save(in memOffset, dataSectionSlice); + vm.EvmState.Memory.Save(in memOffset, dataSectionSlice); if (vm.TxTracer.IsTracingInstructions) { vm.TxTracer.ReportMemoryChange(memOffset, dataSectionSlice); @@ -139,7 +139,7 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta [SkipLocalsInit] public static EvmExceptionType InstructionRelativeJump(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -154,7 +154,7 @@ public static EvmExceptionType InstructionRelativeJump(VirtualMachine vm, ref Ev [SkipLocalsInit] public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -174,7 +174,7 @@ public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref [SkipLocalsInit] public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -199,7 +199,7 @@ public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmSt [SkipLocalsInit] public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -214,10 +214,10 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev return EvmExceptionType.StackOverflow; } - if (vm.State.ReturnStackHead == Eof1.RETURN_STACK_MAX_HEIGHT) + if (vm.EvmState.ReturnStackHead == Eof1.RETURN_STACK_MAX_HEIGHT) return EvmExceptionType.InvalidSubroutineEntry; - vm.State.ReturnStack[vm.State.ReturnStackHead++] = new EvmState.ReturnState + vm.EvmState.ReturnStack[vm.EvmState.ReturnStackHead++] = new EvmState.ReturnState { Index = vm.SectionIndex, Height = stack.Head - inputCount, @@ -233,7 +233,7 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev [SkipLocalsInit] public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -241,7 +241,7 @@ public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref (_, int outputCount, _) = codeInfo.GetSectionMetadata(vm.SectionIndex); - EvmState.ReturnState stackFrame = vm.State.ReturnStack[--vm.State.ReturnStackHead]; + EvmState.ReturnState stackFrame = vm.EvmState.ReturnStack[--vm.EvmState.ReturnStackHead]; vm.SectionIndex = stackFrame.Index; programCounter = stackFrame.Offset; @@ -251,7 +251,7 @@ public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref [SkipLocalsInit] public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -273,7 +273,7 @@ public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref Ev [SkipLocalsInit] public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -290,7 +290,7 @@ public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack s [SkipLocalsInit] public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -307,7 +307,7 @@ public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack [SkipLocalsInit] public static EvmExceptionType InstructionExchange(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -331,13 +331,13 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt vm.ReturnData = null; IReleaseSpec spec = vm.Spec; - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) return EvmExceptionType.BadInstruction; - if (vm.State.IsStatic) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic) return EvmExceptionType.StaticCallViolation; - ref readonly ExecutionEnvironment env = ref vm.State.Env; + ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; EofCodeInfo container = env.CodeInfo as EofCodeInfo; ExecutionType currentContext = ExecutionType.EOFCREATE; @@ -357,7 +357,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt stack.PopUInt256(out UInt256 dataSize); // 4 - perform (and charge for) memory expansion using [input_offset, input_size] - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in dataOffset, dataSize)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataSize)) return EvmExceptionType.OutOfGas; // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header @@ -389,7 +389,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt } // 8 - caller’s memory slice [input_offset:input_size] is used as calldata - Span calldata = vm.State.Memory.LoadSpan(dataOffset, dataSize); + Span calldata = vm.EvmState.Memory.LoadSpan(dataOffset, dataSize); // 9 - execute the container and deduct gas for execution. The 63/64th rule from EIP-150 applies. long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; @@ -411,7 +411,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt if (spec.UseHotAndColdStorage) { // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 - vm.State.AccessTracker.WarmUp(contractAddress); + vm.EvmState.AccessTracker.WarmUp(contractAddress); } @@ -458,11 +458,11 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt outputDestination: 0, outputLength: 0, executionType: currentContext, - isStatic: vm.State.IsStatic, + isStatic: vm.EvmState.IsStatic, isCreateOnPreExistingAccount: accountExists, in snapshot, env: in callEnv, - in vm.State.AccessTracker + in vm.EvmState.AccessTracker ); return EvmExceptionType.None; @@ -471,14 +471,14 @@ in vm.State.AccessTracker [SkipLocalsInit] public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - if (!vm.State.ExecutionType.IsAnyCreateEof()) + if (!vm.EvmState.ExecutionType.IsAnyCreateEof()) return EvmExceptionType.BadInstruction; if (!UpdateGas(GasCostOf.ReturnContract, ref gasAvailable)) return EvmExceptionType.OutOfGas; IReleaseSpec spec = vm.Spec; - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; byte sectionIdx = codeInfo.CodeSection.Span[programCounter++]; ReadOnlyMemory deployCode = codeInfo.ContainerSection[(Range)codeInfo.ContainerSectionOffset(sectionIdx)]; @@ -488,7 +488,7 @@ public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref stack.PopUInt256(out UInt256 b); ReadOnlyMemory auxData = ReadOnlyMemory.Empty; - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; int projectedNewSize = (int)b + deploycodeInfo.DataSection.Length; if (projectedNewSize < deploycodeInfo.EofContainer.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) @@ -496,7 +496,7 @@ public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref return EvmExceptionType.AccessViolation; } - vm.ReturnDataBuffer = vm.State.Memory.Load(a, b); + vm.ReturnDataBuffer = vm.EvmState.Memory.Load(a, b); vm.ReturnData = deploycodeInfo; return EvmExceptionType.None; @@ -506,7 +506,7 @@ public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref public static EvmExceptionType InstructionReturnDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; - ICodeInfo codeInfo = vm.State.Env.CodeInfo; + ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (!spec.IsEofEnabled || codeInfo.Version == 0) return EvmExceptionType.BadInstruction; @@ -552,7 +552,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, IReleaseSpec spec = vm.Spec; vm.ReturnData = null; - ref readonly ExecutionEnvironment env = ref vm.State.Env; + ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; IWorldState state = vm.WorldState; // Instruction is undefined in legacy code and only available in EOF @@ -580,7 +580,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, // 3. If value is non-zero: // a: Halt with exceptional failure if the current frame is in static-mode. - if (vm.State.IsStatic && !callValue.IsZero) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic && !callValue.IsZero) return EvmExceptionType.StaticCallViolation; // b. Charge CALL_VALUE_COST gas. if (!callValue.IsZero && !UpdateGas(GasCostOf.CallValue, ref gasAvailable)) return EvmExceptionType.OutOfGas; @@ -599,7 +599,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, : codeSource; // 5. Perform (and charge for) memory expansion using [input_offset, input_size]. - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in dataOffset, in dataLength)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, in dataLength)) return EvmExceptionType.OutOfGas; // 1. Charge WARM_STORAGE_READ_COST (100) gas. // 6. If target_address is not in the warm_account_list, charge COLD_ACCOUNT_ACCESS - WARM_STORAGE_READ_COST (2500) gas. if (!ChargeAccountAccessGas(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; @@ -668,7 +668,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, // 10. Perform the call with the available gas and configuration. if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; - ReadOnlyMemory callData = vm.State.Memory.Load(in dataOffset, dataLength); + ReadOnlyMemory callData = vm.EvmState.Memory.Load(in dataOffset, dataLength); Snapshot snapshot = state.TakeSnapshot(); state.SubtractFromBalance(caller, callValue, spec); @@ -691,11 +691,11 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, outputDestination: 0, outputLength: 0, TOpEofCall.ExecutionType, - isStatic: TOpEofCall.IsStatic || vm.State.IsStatic, + isStatic: TOpEofCall.IsStatic || vm.EvmState.IsStatic, isCreateOnPreExistingAccount: false, in snapshot, env: in callEnv, - in vm.State.AccessTracker + in vm.EvmState.AccessTracker ); return EvmExceptionType.None; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs index 662e72d2bd5..646ef460e83 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs @@ -36,7 +36,7 @@ public static EvmExceptionType InstructionBlobHash(VirtualMachine vm, ref EvmSta if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - byte[][] versionedHashes = vm.State.Env.TxExecutionContext.BlobVersionedHashes; + byte[][] versionedHashes = vm.EvmState.Env.TxExecutionContext.BlobVersionedHashes; if (versionedHashes is not null && result < versionedHashes.Length) { @@ -60,7 +60,7 @@ public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmSt if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; long number = a > long.MaxValue ? long.MaxValue : (long)a; - Hash256? blockHash = vm.BlockhashProvider.GetBlockhash(vm.State.Env.TxExecutionContext.BlockExecutionContext.Header, number); + Hash256? blockHash = vm.BlockhashProvider.GetBlockhash(vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header, number); stack.PushBytes(blockHash is not null ? blockHash.Bytes : BytesZero32); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs index 8389feec3e9..11486a7d6e0 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs @@ -32,7 +32,7 @@ public static EvmExceptionType InstructionJump(VirtualMachine vm, ref EvmStack s { gasAvailable -= GasCostOf.Mid; if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - if (!Jump(result, ref programCounter, in vm.State.Env)) return EvmExceptionType.InvalidJumpDestination; + if (!Jump(result, ref programCounter, in vm.EvmState.Env)) return EvmExceptionType.InvalidJumpDestination; return EvmExceptionType.None; } @@ -47,7 +47,7 @@ public static EvmExceptionType InstructionJumpIf(VirtualMachine vm, ref EvmStack if (Unsafe.IsNullRef(in condition)) return EvmExceptionType.StackUnderflow; if (Unsafe.As>(ref condition) != default) { - if (!Jump(result, ref programCounter, in vm.State.Env)) return EvmExceptionType.InvalidJumpDestination; + if (!Jump(result, ref programCounter, in vm.EvmState.Env)) return EvmExceptionType.InvalidJumpDestination; } return EvmExceptionType.None; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index 7629df55cc8..d5b2d7177da 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -86,7 +86,7 @@ public static EvmExceptionType InstructionPush(VirtualMachine vm, ref { gasAvailable -= GasCostOf.VeryLow; - ReadOnlySpan code = vm.State.Env.CodeInfo.CodeSection.Span; + ReadOnlySpan code = vm.EvmState.Env.CodeInfo.CodeSection.Span; int length = TOpCount.Count; int usedFromCode = Math.Min(code.Length - programCounter, length); @@ -101,7 +101,7 @@ public static EvmExceptionType InstructionPush(VirtualMachine vm, ref public static EvmExceptionType InstructionLog(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCount : struct, IOpCount { - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; if (!stack.PopUInt256(out UInt256 position)) return EvmExceptionType.StackUnderflow; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index 8624956f468..b91de51b91a 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -28,7 +28,7 @@ public static EvmExceptionType InstructionTLoad(VirtualMachine vm, ref EvmStack gasAvailable -= GasCostOf.TLoad; if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - StorageCell storageCell = new(vm.State.Env.ExecutingAccount, result); + StorageCell storageCell = new(vm.EvmState.Env.ExecutingAccount, result); ReadOnlySpan value = vm.WorldState.GetTransientState(in storageCell); stack.PushBytes(value); @@ -46,7 +46,7 @@ public static EvmExceptionType InstructionTLoad(VirtualMachine vm, ref EvmStack public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.TstoreOpcode++; - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; @@ -76,7 +76,7 @@ public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack if (!stack.PopUInt256(out UInt256 c)) return EvmExceptionType.StackUnderflow; gasAvailable -= GasCostOf.VeryLow + GasCostOf.VeryLow * EvmPooledMemory.Div32Ceiling(c); - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, UInt256.Max(b, a), c)) return EvmExceptionType.OutOfGas; Span bytes = vmState.Memory.LoadSpan(in b, c); @@ -96,7 +96,7 @@ public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { Metrics.IncrementSStoreOpcode(); - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; IReleaseSpec spec = vm.Spec; // fail fast before the first storage read if gas is not enough even for reset @@ -244,7 +244,7 @@ internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStac gasAvailable -= spec.GetSLoadCost(); if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - StorageCell storageCell = new(vm.State.Env.ExecutingAccount, result); + StorageCell storageCell = new(vm.EvmState.Env.ExecutingAccount, result); if (!ChargeStorageAccessGas( ref gasAvailable, vm, @@ -269,7 +269,7 @@ internal static bool ChargeStorageAccessGas( StorageAccessType storageAccessType, IReleaseSpec spec) { - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; bool result = true; if (spec.UseHotAndColdStorage) { diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index cb3f50d7b09..79b5167fbc0 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -267,7 +267,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) [SkipLocalsInit] public static EvmExceptionType InstructionStop(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - if (vm.State.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) + if (vm.EvmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) { return EvmExceptionType.BadInstruction; } @@ -282,12 +282,12 @@ public static EvmExceptionType InstructionRevert(VirtualMachine vm, ref EvmStack !stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; - if (!UpdateMemoryCost(vm.State, ref gasAvailable, in position, in length)) + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in position, in length)) { return EvmExceptionType.OutOfGas; } - vm.ReturnData = vm.State.Memory.Load(in position, in length).ToArray(); + vm.ReturnData = vm.EvmState.Memory.Load(in position, in length).ToArray(); return EvmExceptionType.Revert; } @@ -297,7 +297,7 @@ private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref E { Metrics.IncrementSelfDestructs(); - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; IReleaseSpec spec = vm.Spec; IWorldState state = vm.WorldState; @@ -350,7 +350,7 @@ private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref E public static EvmExceptionType InstructionPrevRandao(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; - BlockHeader header = vm.State.Env.TxExecutionContext.BlockExecutionContext.Header; + BlockHeader header = vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header; if (header.IsPostMerge) { stack.PushBytes(header.Random.Bytes); @@ -474,7 +474,7 @@ public static EvmExceptionType InstructionKeccak256(VirtualMachine vm, ref EvmSt if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; gasAvailable -= GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in b); - EvmState vmState = vm.State; + EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; Span bytes = vmState.Memory.LoadSpan(in a, b); @@ -489,7 +489,7 @@ public static EvmExceptionType InstructionCallDataLoad(VirtualMachine vm, ref Ev gasAvailable -= GasCostOf.VeryLow; if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - stack.PushBytes(vm.State.Env.InputData.SliceWithZeroPadding(result, 32)); + stack.PushBytes(vm.EvmState.Env.InputData.SliceWithZeroPadding(result, 32)); return EvmExceptionType.None; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3bd54fc23f0..807eb70a6a3 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -4,14 +4,10 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.Precompiles; @@ -168,13 +164,13 @@ public interface IIsTracing { } public ReadOnlySpan ChainId => _chainId; public ReadOnlyMemory ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } public object ReturnData { get => _returnData; set => _returnData = value; } - object _returnData; + private object _returnData; public IBlockhashProvider BlockhashProvider => _blockhashProvider; - public EvmState State => _vmState; - EvmState _vmState; + public EvmState EvmState => _vmState; + private EvmState _vmState; public int SectionIndex { get => _sectionIndex; set => _sectionIndex = value; } - int _sectionIndex; + private int _sectionIndex; OpCode[] _opcodeMethods; @@ -592,82 +588,12 @@ private static bool UpdateGas(long gasCost, ref long gasAvailable) return true; } - private static void UpdateGasUp(long refund, ref long gasAvailable) - { - gasAvailable += refund; - } - - private bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Address address, bool chargeForDelegation, IReleaseSpec spec, bool chargeForWarm = true) - { - if (!spec.UseHotAndColdStorage) - { - return true; - } - bool notOutOfGas = ChargeAccountGas(ref gasAvailable, vmState, address, spec); - return notOutOfGas - && (!chargeForDelegation - || !vmState.Env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(_state, address, spec, out Address delegated) - || ChargeAccountGas(ref gasAvailable, vmState, delegated, spec)); - - bool ChargeAccountGas(ref long gasAvailable, EvmState vmState, Address address, IReleaseSpec spec) - { - bool result = true; - if (_txTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list - { - vmState.AccessTracker.WarmUp(address); - } - - if (vmState.AccessTracker.IsCold(address) && !address.IsPrecompile(spec)) - { - result = UpdateGas(GasCostOf.ColdAccountAccess, ref gasAvailable); - vmState.AccessTracker.WarmUp(address); - } - else if (chargeForWarm) - { - result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); - } - return result; - } - } - private enum StorageAccessType { SLOAD, SSTORE } - private bool ChargeStorageAccessGas( - ref long gasAvailable, - EvmState vmState, - in StorageCell storageCell, - StorageAccessType storageAccessType, - IReleaseSpec spec) - { - // Console.WriteLine($"Accessing {storageCell} {storageAccessType}"); - - bool result = true; - if (spec.UseHotAndColdStorage) - { - if (_txTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list - { - vmState.AccessTracker.WarmUp(in storageCell); - } - - if (vmState.AccessTracker.IsCold(in storageCell)) - { - result = UpdateGas(GasCostOf.ColdSLoad, ref gasAvailable); - vmState.AccessTracker.WarmUp(in storageCell); - } - else if (storageAccessType == StorageAccessType.SLOAD) - { - // we do not charge for WARM_STORAGE_READ_COST in SSTORE scenario - result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); - } - } - - return result; - } - private CallResult ExecutePrecompile(EvmState state) { ReadOnlyMemory callData = state.Env.InputData; @@ -794,6 +720,9 @@ private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailab ReadOnlySpan codeSection = GetInstructions(codeInfo); EvmExceptionType exceptionType = EvmExceptionType.None; +#if DEBUG + DebugTracer? debugger = _txTracer.GetTracer(); +#endif var tracingInstructions = _txTracer.IsTracingInstructions; var isCancelable = _txTracer.IsCancelable; @@ -806,6 +735,9 @@ private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailab // opcodes can change the program counter (e.g. Push, Jump, etc) while ((uint)programCounter < (uint)codeSection.Length) { +#if DEBUG + debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); +#endif // Get the opcode at the current program counter Instruction instruction = codeSection[programCounter]; @@ -849,6 +781,9 @@ private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailab return CallResult.Empty(codeInfo.Version); DataReturn: +#if DEBUG + debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); +#endif if (_returnData is EvmState state) { return new CallResult(state); @@ -875,118 +810,8 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(codeBytes)), [DoesNotReturn] static void ThrowOperationCanceledException() => throw new OperationCanceledException("Cancellation Requested"); - } - /* - [SkipLocalsInit] - private unsafe CallResult ExecuteCodeTraced(scoped ref EvmStack stack, long gasAvailable) - { - _returnData = null; - ICodeInfo codeInfo = _vmState.Env.CodeInfo; - - int programCounter = _vmState.ProgramCounter; - _sectionIndex = _vmState.FunctionIndex; - - ReadOnlySpan codeSection = codeInfo.CodeSection.Span; - - EvmExceptionType exceptionType = EvmExceptionType.None; - bool isRevert = false; -#if DEBUG - DebugTracer? debugger = _txTracer.GetTracer(); -#endif - OpCode[] opcodes = _opcodes; - //bool isCancelable = _txTracer.IsCancelable; - uint codeLength = (uint)codeSection.Length; - while ((uint)programCounter < codeLength) - { -#if DEBUG - debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); -#endif - Instruction instruction = (Instruction)codeSection[programCounter]; - - //if (isCancelable && _txTracer.IsCancelled) - //{ - // ThrowOperationCanceledException(); - //} - - //// Evaluated to constant at compile time and code elided if not tracing - //if (typeof(TTracingInstructions) == typeof(IsTracing)) - // StartInstructionTrace(instruction, _vmState, gasAvailable, programCounter, in stack); - - programCounter++; - - OpCode opcode = opcodes[(int)instruction]; - exceptionType = opcode(this, ref stack, ref gasAvailable, ref programCounter); - - if (exceptionType != EvmExceptionType.None) break; - if (gasAvailable < 0) goto OutOfGas; - - if (_returnData is not null) - { - if (!ReferenceEquals(_returnData, CallResult.BoxedEmpty)) - { - goto DataReturnNoTrace; - } - // Non contract call continue rather than constructing a new frame - _returnData = null; - } - - //if (typeof(TTracingInstructions) == typeof(IsTracing)) - //{ - // EndInstructionTrace(gasAvailable, _vmState.Memory.Size); - //} - } - - if (exceptionType == EvmExceptionType.Stop) goto EmptyReturn; - if (exceptionType == EvmExceptionType.Revert) - { - isRevert = true; - goto DataReturn; - } - if (exceptionType != EvmExceptionType.None) goto ReturnFailure; - goto EmptyReturnNoTrace; - - // Common exit errors, goto labels to reduce in loop code duplication and to keep loop body smaller - EmptyReturn: - if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, _vmState.Memory.Size); - EmptyReturnNoTrace: - // Ensure gas is positive before updating state - if (gasAvailable < 0) goto OutOfGas; - UpdateCurrentState(_vmState, programCounter, gasAvailable, stack.Head, _sectionIndex); -#if DEBUG - debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); -#endif - return CallResult.Empty(codeInfo.Version); - DataReturn: - if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, _vmState.Memory.Size); - DataReturnNoTrace: - // Ensure gas is positive before updating state - if (gasAvailable < 0) goto OutOfGas; - UpdateCurrentState(_vmState, programCounter, gasAvailable, stack.Head, _sectionIndex); - - if (_returnData is EvmState state) - { - return new CallResult(state); - } - else if (_returnData is EofCodeInfo eofCodeInfo) - { - return new CallResult(eofCodeInfo, _returnDataBuffer, null, codeInfo.Version); - } - return new CallResult(null, (byte[])_returnData, null, codeInfo.Version, shouldRevert: isRevert); - - OutOfGas: - exceptionType = EvmExceptionType.OutOfGas; - goto ReturnFailure; - ReturnFailure: - return GetFailureReturn(gasAvailable, exceptionType); - - //[DoesNotReturn] - //static void ThrowOperationCanceledException() => - // throw new OperationCanceledException("Cancellation Requested"); - } - */ - private CallResult GetFailureReturn(long gasAvailable, EvmExceptionType exceptionType) { if (_txTracer.IsTracingInstructions) EndInstructionTraceError(gasAvailable, exceptionType); @@ -1024,27 +849,6 @@ private static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in return memoryCost == 0L || UpdateGas(memoryCost, ref gasAvailable); } - private static bool Jump(CodeInfo codeinfo, in UInt256 jumpDest, ref int programCounter, in ExecutionEnvironment env) - { - if (jumpDest > int.MaxValue) - { - // https://github.com/NethermindEth/nethermind/issues/140 - // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 0xf435a354924097686ea88dab3aac1dd464e6a3b387c77aeee94145b0fa5a63d2 mainnet - return false; - } - - int jumpDestInt = (int)jumpDest; - if (!codeinfo.ValidateJump(jumpDestInt)) - { - // https://github.com/NethermindEth/nethermind/issues/140 - // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 61363 Ropsten - return false; - } - - programCounter = jumpDestInt; - return true; - } - [MethodImpl(MethodImplOptions.NoInlining)] private void StartInstructionTrace(Instruction instruction, long gasAvailable, int programCounter, in EvmStack stackValue) { @@ -1079,17 +883,4 @@ private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExc _txTracer.ReportOperationRemainingGas(gasAvailable); _txTracer.ReportOperationError(evmExceptionType); } - - private static ExecutionType GetCallExecutionType(Instruction instruction, bool isPostMerge = false) - => instruction switch - { - Instruction.CALL => ExecutionType.CALL, - Instruction.DELEGATECALL => ExecutionType.DELEGATECALL, - Instruction.STATICCALL => ExecutionType.STATICCALL, - Instruction.CALLCODE => ExecutionType.CALLCODE, - Instruction.EXTCALL => ExecutionType.EOFCALL, - Instruction.EXTDELEGATECALL => ExecutionType.EOFDELEGATECALL, - Instruction.EXTSTATICCALL => ExecutionType.EOFSTATICCALL, - _ => throw new NotSupportedException($"Execution type is undefined for {instruction.GetName(isPostMerge)}") - }; } From 5cda52b0ee04a0c07935397fe39b5a14e003c81c Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 31 Jan 2025 20:04:35 +0000 Subject: [PATCH 180/255] Try up --- .../Instructions/EvmInstructions.Extras.cs | 6 +- .../Nethermind.Evm/VirtualMachine.cs | 181 +++++++++--------- 2 files changed, 90 insertions(+), 97 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs index 646ef460e83..03c90f26204 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs @@ -3,13 +3,13 @@ using System; using System.Runtime.CompilerServices; +using Nethermind.Core.Crypto; using Nethermind.Core.Specs; namespace Nethermind.Evm; -using Int256; -using Nethermind.Core.Crypto; using static Nethermind.Evm.VirtualMachine; +using Int256; internal sealed partial class EvmInstructions { @@ -60,7 +60,7 @@ public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmSt if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; long number = a > long.MaxValue ? long.MaxValue : (long)a; - Hash256? blockHash = vm.BlockhashProvider.GetBlockhash(vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header, number); + Hash256? blockHash = vm.BlockHashProvider.GetBlockhash(vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header, number); stack.PushBytes(blockHash is not null ? blockHash.Bytes : BytesZero32); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 807eb70a6a3..fc069c92a4a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -6,14 +6,18 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Evm.CodeAnalysis; +using Nethermind.Evm.EvmObjectFormat; +using Nethermind.Evm.EvmObjectFormat.Handlers; using Nethermind.Evm.Precompiles; using Nethermind.Evm.Tracing; using Nethermind.Logging; using Nethermind.State; + using static Nethermind.Evm.EvmObjectFormat.EofValidator; #if DEBUG @@ -21,16 +25,11 @@ #endif [assembly: InternalsVisibleTo("Nethermind.Evm.Test")] - namespace Nethermind.Evm; using unsafe OpCode = delegate*; using Int256; -using Nethermind.Evm.EvmObjectFormat; -using Nethermind.Evm.EvmObjectFormat.Handlers; -using System.Runtime.InteropServices; - public sealed unsafe class VirtualMachine : IVirtualMachine { public const int MaxCallDepth = Eof1.RETURN_STACK_MAX_HEIGHT; @@ -61,100 +60,18 @@ public sealed unsafe class VirtualMachine : IVirtualMachine internal static readonly PrecompileExecutionFailureException PrecompileExecutionFailureException = new(); internal static readonly OutOfGasException PrecompileOutOfGasException = new(); - //private readonly IVirtualMachine _evm; - - public VirtualMachine( - IBlockhashProvider? blockhashProvider, - ISpecProvider? specProvider, - ICodeInfoRepository codeInfoRepository, - ILogManager? logManager) - { - _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - _blockhashProvider = blockhashProvider ?? throw new ArgumentNullException(nameof(blockhashProvider)); - _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); - _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); - } - - internal 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); // TODO: use these to avoid CALL POP attacks - public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks - public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); - public static CallResult InvalidAddressRange => new(EvmExceptionType.AddressOutOfRange); - public static CallResult Empty(int fromVersion) => new(null, default, null, fromVersion); - - public CallResult(EvmState stateToExecute) - { - StateToExecute = stateToExecute; - Output = (null, Array.Empty()); - PrecompileSuccess = null; - ShouldRevert = false; - ExceptionType = EvmExceptionType.None; - } - - public CallResult(ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) - { - StateToExecute = null; - Output = (null, output); - PrecompileSuccess = precompileSuccess; - ShouldRevert = shouldRevert; - ExceptionType = exceptionType; - FromVersion = fromVersion; - } - - public CallResult(ICodeInfo? container, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) - { - StateToExecute = null; - Output = (container, output); - PrecompileSuccess = precompileSuccess; - ShouldRevert = shouldRevert; - ExceptionType = exceptionType; - FromVersion = fromVersion; - } - - private CallResult(EvmExceptionType exceptionType) - { - StateToExecute = null; - Output = (null, StatusCode.FailureBytes); - PrecompileSuccess = null; - ShouldRevert = false; - ExceptionType = exceptionType; - } - - public EvmState? StateToExecute { get; } - public (ICodeInfo Container, ReadOnlyMemory Bytes) Output { get; } - public EvmExceptionType ExceptionType { get; } - public bool ShouldRevert { get; } - public bool? PrecompileSuccess { get; } // TODO: check this behaviour as it seems it is required and previously that was not the case - public bool IsReturn => StateToExecute is null; - public bool IsException => ExceptionType != EvmExceptionType.None; - - public int FromVersion { get; } - } - - public interface IIsTracing { } - public readonly struct NotTracing : IIsTracing { } - public readonly struct IsTracing : IIsTracing { } - private readonly byte[] _chainId; - private readonly IBlockhashProvider _blockhashProvider; + private readonly IBlockhashProvider _blockHashProvider; private readonly ISpecProvider _specProvider; private readonly ILogger _logger; - private IWorldState _state = null!; private readonly Stack _stateStack = new(); + private readonly ICodeInfoRepository _codeInfoRepository; + + private IWorldState _state = null!; private (Address Address, bool ShouldDelete) _parityTouchBugAccount = (Address.FromNumber(3), false); private ReadOnlyMemory _returnDataBuffer = Array.Empty(); private ITxTracer _txTracer = NullTxTracer.Instance; - private readonly ICodeInfoRepository _codeInfoRepository; private IReleaseSpec _spec; public ICodeInfoRepository CodeInfoRepository => _codeInfoRepository; @@ -165,14 +82,27 @@ public interface IIsTracing { } public ReadOnlyMemory ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } public object ReturnData { get => _returnData; set => _returnData = value; } private object _returnData; - public IBlockhashProvider BlockhashProvider => _blockhashProvider; + public IBlockhashProvider BlockHashProvider => _blockHashProvider; public EvmState EvmState => _vmState; private EvmState _vmState; public int SectionIndex { get => _sectionIndex; set => _sectionIndex = value; } private int _sectionIndex; - OpCode[] _opcodeMethods; + private OpCode[] _opcodeMethods; + + public VirtualMachine( + IBlockhashProvider? blockHashProvider, + ISpecProvider? specProvider, + ICodeInfoRepository codeInfoRepository, + ILogManager? logManager) + { + _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); + _blockHashProvider = blockHashProvider ?? throw new ArgumentNullException(nameof(blockHashProvider)); + _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); + _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); + _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); + } public VirtualMachine( IBlockhashProvider? blockhashProvider, @@ -181,7 +111,7 @@ public VirtualMachine( ILogger? logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _blockhashProvider = blockhashProvider ?? throw new ArgumentNullException(nameof(blockhashProvider)); + _blockHashProvider = blockhashProvider ?? throw new ArgumentNullException(nameof(blockhashProvider)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); @@ -883,4 +813,67 @@ private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExc _txTracer.ReportOperationRemainingGas(gasAvailable); _txTracer.ReportOperationError(evmExceptionType); } + + internal 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); // TODO: use these to avoid CALL POP attacks + public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks + public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); + public static CallResult InvalidAddressRange => new(EvmExceptionType.AddressOutOfRange); + public static CallResult Empty(int fromVersion) => new(null, default, null, fromVersion); + + public CallResult(EvmState stateToExecute) + { + StateToExecute = stateToExecute; + Output = (null, Array.Empty()); + PrecompileSuccess = null; + ShouldRevert = false; + ExceptionType = EvmExceptionType.None; + } + + public CallResult(ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + { + StateToExecute = null; + Output = (null, output); + PrecompileSuccess = precompileSuccess; + ShouldRevert = shouldRevert; + ExceptionType = exceptionType; + FromVersion = fromVersion; + } + + public CallResult(ICodeInfo? container, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + { + StateToExecute = null; + Output = (container, output); + PrecompileSuccess = precompileSuccess; + ShouldRevert = shouldRevert; + ExceptionType = exceptionType; + FromVersion = fromVersion; + } + + private CallResult(EvmExceptionType exceptionType) + { + StateToExecute = null; + Output = (null, StatusCode.FailureBytes); + PrecompileSuccess = null; + ShouldRevert = false; + ExceptionType = exceptionType; + } + + public EvmState? StateToExecute { get; } + public (ICodeInfo Container, ReadOnlyMemory Bytes) Output { get; } + public EvmExceptionType ExceptionType { get; } + public bool ShouldRevert { get; } + public bool? PrecompileSuccess { get; } // TODO: check this behaviour as it seems it is required and previously that was not the case + public bool IsReturn => StateToExecute is null; + public bool IsException => ExceptionType != EvmExceptionType.None; + public int FromVersion { get; } + } } From 6d1617f4ebc0d473e848b6db9a50a121c8c0a5c4 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 31 Jan 2025 20:16:42 +0000 Subject: [PATCH 181/255] Spec changes --- .../EvmInstructions.Environment.cs | 41 ++++++++++++++----- .../Instructions/EvmInstructions.cs | 8 ++-- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index 52661832d3b..27f56b4c591 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -28,8 +28,6 @@ public static EvmExceptionType InstructionPop(VirtualMachine _, ref EvmStack sta [SkipLocalsInit] public static EvmExceptionType InstructionChainId(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - if (!vm.Spec.ChainIdOpcodeEnabled) return EvmExceptionType.BadInstruction; - gasAvailable -= GasCostOf.Base; stack.PushBytes(vm.ChainId); @@ -70,17 +68,38 @@ public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref Evm } else { - if (spec.IsEofEnabled) + stack.PushBytes(state.GetCodeHash(address).Bytes); + } + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + IReleaseSpec spec = vm.Spec; + gasAvailable -= spec.GetExtCodeHashCost(); + + Address address = stack.PopAddress(); + if (address is null) return EvmExceptionType.StackUnderflow; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + + IWorldState state = vm.WorldState; + if (state.IsDeadAccount(address)) + { + stack.PushZero(); + } + else + { + Memory code = state.GetCode(address); + if (EofValidator.IsEof(code, out _)) { - Memory code = state.GetCode(address); - if (EofValidator.IsEof(code, out _)) - { - stack.PushBytes(EofHash256); - return EvmExceptionType.None; - } + stack.PushBytes(EofHash256); + } + else + { + stack.PushBytes(state.GetCodeHash(address).Bytes); } - - stack.PushBytes(state.GetCodeHash(address).Bytes); } return EvmExceptionType.None; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 79b5167fbc0..9c62cb86307 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -80,7 +80,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) } if (spec.ExtCodeHashOpcodeEnabled) { - lookup[(int)Instruction.EXTCODEHASH] = &InstructionExtCodeHash; + lookup[(int)Instruction.EXTCODEHASH] = spec.IsEofEnabled ? &InstructionExtCodeHashEof : &InstructionExtCodeHash; } lookup[(int)Instruction.BLOCKHASH] = &InstructionBlockHash; @@ -90,8 +90,10 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.NUMBER] = &InstructionEnvUInt256; lookup[(int)Instruction.PREVRANDAO] = &InstructionPrevRandao; lookup[(int)Instruction.GASLIMIT] = &InstructionEnvUInt256; - lookup[(int)Instruction.CHAINID] = &InstructionChainId; - + if (spec.ChainIdOpcodeEnabled) + { + lookup[(int)Instruction.CHAINID] = &InstructionChainId; + } if (spec.SelfBalanceOpcodeEnabled) { lookup[(int)Instruction.SELFBALANCE] = &InstructionSelfBalance; From cbd8358646fd984cd0c5b30a74cf09775f2bf3bb Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 31 Jan 2025 23:27:23 +0000 Subject: [PATCH 182/255] Reapply fixes --- .../Instructions/EvmInstructions.Eof.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 250f66e6e94..99bfbb757cf 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -564,25 +564,32 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, stack.PopUInt256(out UInt256 dataOffset); stack.PopUInt256(out UInt256 dataLength); + UInt256 transferValue; UInt256 callValue; 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 callValue)) + else if (stack.PopUInt256(out transferValue)) + { + callValue = transferValue; + } + else { return EvmExceptionType.StackUnderflow; } // 3. If value is non-zero: // a: Halt with exceptional failure if the current frame is in static-mode. - if (vm.EvmState.IsStatic && !callValue.IsZero) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic && !transferValue.IsZero) return EvmExceptionType.StaticCallViolation; // b. Charge CALL_VALUE_COST gas. - if (!callValue.IsZero && !UpdateGas(GasCostOf.CallValue, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (typeof(TOpEofCall) == typeof(OpEofCall) && !transferValue.IsZero && !UpdateGas(GasCostOf.CallValue, ref gasAvailable)) return EvmExceptionType.OutOfGas; // 4. If target_address has any of the high 12 bytes set to a non-zero value // (i.e. it does not contain a 20-byte address) @@ -593,7 +600,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, } Address caller = typeof(TOpEofCall) == typeof(OpEofDelegateCall) ? env.Caller : env.ExecutingAccount; - Address codeSource = new Address(targetBytes[12..].ToArray()); + Address codeSource = new (targetBytes[12..].ToArray()); Address target = typeof(TOpEofCall) == typeof(OpEofDelegateCall) ? env.ExecutingAccount : codeSource; @@ -605,7 +612,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, if (!ChargeAccountAccessGas(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; if ((!spec.ClearEmptyAccountWhenTouched && !state.AccountExists(codeSource)) - || (spec.ClearEmptyAccountWhenTouched && callValue != 0 && state.IsDeadAccount(codeSource))) + || (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && state.IsDeadAccount(codeSource))) { // 7. If target_address is not in the state and the call configuration would result in account creation, // charge ACCOUNT_CREATION_COST (25000) gas. (The only such case in this EIP is if value is non-zero.) @@ -620,7 +627,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, // b: Balance of the current account is less than value. // c: Current call stack depth equals 1024. if (callGas < GasCostOf.CallStipend || - (!callValue.IsZero && state.GetBalance(env.ExecutingAccount) < callValue) || + (!transferValue.IsZero && state.GetBalance(env.ExecutingAccount) < transferValue) || env.CallDepth >= MaxCallDepth) { vm.ReturnData = null; @@ -645,7 +652,8 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, //{ // _logger.Trace($"caller {caller}"); // _logger.Trace($"target {codeSource}"); - // _logger.Trace($"value {callValue}"); + // _logger.Trace($"transferValue {transferValue}"); + // _logger.Trace($"callValue {callValue}"); //} ICodeInfo targetCodeInfo = vm.CodeInfoRepository.GetCachedCodeInfo(state, codeSource, spec); @@ -671,7 +679,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, ReadOnlyMemory callData = vm.EvmState.Memory.Load(in dataOffset, dataLength); Snapshot snapshot = state.TakeSnapshot(); - state.SubtractFromBalance(caller, callValue, spec); + state.SubtractFromBalance(caller, transferValue, spec); ExecutionEnvironment callEnv = new ( @@ -680,7 +688,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, caller: caller, codeSource: codeSource, executingAccount: target, - transferValue: callValue, + transferValue: transferValue, value: callValue, inputData: callData, codeInfo: targetCodeInfo From a7267a5f66fc8bade369985fceb924ee06993534 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 31 Jan 2025 23:35:13 +0000 Subject: [PATCH 183/255] formating --- .../Nethermind.Evm/Instructions/EvmInstructions.Eof.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 99bfbb757cf..06cb83dfad0 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -600,7 +600,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, } Address caller = typeof(TOpEofCall) == typeof(OpEofDelegateCall) ? env.Caller : env.ExecutingAccount; - Address codeSource = new (targetBytes[12..].ToArray()); + Address codeSource = new(targetBytes[12..].ToArray()); Address target = typeof(TOpEofCall) == typeof(OpEofDelegateCall) ? env.ExecutingAccount : codeSource; From 665c79d6f8f73f8f560a596ce99fbced167a67c3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 00:19:49 +0000 Subject: [PATCH 184/255] tweak --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index fc069c92a4a..ecff746015c 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -227,7 +227,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer { if (_txTracer.IsTracingActions) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(_spec, callResult.Output.Bytes.Length); + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); if (callResult.IsException) { From df9ffff5781a4ce91edc4ddc0c5d68dfab3ceccd Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 02:04:39 +0000 Subject: [PATCH 185/255] Merge conflicts --- .../Nethermind.Evm/CodeInfoRepository.cs | 2 +- .../Instructions/EvmInstructions.Call.cs | 9 +------- .../Instructions/EvmInstructions.CodeCopy.cs | 23 +++++++++++-------- .../Instructions/EvmInstructions.Stack.cs | 2 +- .../Instructions/EvmInstructions.cs | 2 +- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 61dcefd9c58..aa18ad474fd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -66,7 +66,7 @@ public CodeInfoRepository(ConcurrentDictionary(VirtualMachine vm, ref E EvmExceptionType FastCall(VirtualMachine vm, IReleaseSpec spec, in UInt256 transferValue, Address target) { IWorldState state = vm.WorldState; - if (!state.AccountExists(target)) - { - state.CreateAccount(target, transferValue); - } - else - { - state.AddToBalance(target, transferValue, spec); - } + state.AddToBalanceAndCreateIfNotExists(target, transferValue, spec); Metrics.IncrementEmptyCalls(); vm.ReturnData = null; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 1b1a5a529aa..25a47dca1a1 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -57,20 +57,21 @@ public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref Evm { IReleaseSpec spec = vm.Spec; Address address = stack.PopAddress(); - if (address is null) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (address is null) goto StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) goto StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; - gasAvailable -= spec.GetExtCodeCost() + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result); + gasAvailable -= spec.GetExtCodeCost() + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); + if (outOfGas) goto OutOfGas; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; if (!result.IsZero) { - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) goto OutOfGas; - ReadOnlySpan externalCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, spec).MachineCode.Span; + ReadOnlySpan externalCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, followDelegation: false, spec, out _).MachineCode.Span; if (spec.IsEofEnabled && EofValidator.IsEof(externalCode, out _)) { externalCode = EofValidator.MAGIC; @@ -84,6 +85,10 @@ public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref Evm } return EvmExceptionType.None; + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -147,7 +152,7 @@ public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref Evm } } - ReadOnlySpan accountCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, spec).MachineCode.Span; + ReadOnlySpan accountCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, followDelegation: false, spec, out _).MachineCode.Span; if (spec.IsEofEnabled && EofValidator.IsEof(accountCode, out _)) { stack.PushUInt256(2); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index d5b2d7177da..ba67bf8eab8 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -114,7 +114,7 @@ public static EvmExceptionType InstructionLog(VirtualMachine vm, ref E ReadOnlyMemory data = vmState.Memory.Load(in position, length); Hash256[] topics = new Hash256[topicsCount]; - for (int i = 0; i < topicsCount; i++) + for (int i = 0; i < topics.Length; i++) { topics[i] = new Hash256(stack.PopWord256()); } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 9c62cb86307..ab36a742d51 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -312,7 +312,7 @@ private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref E Address inheritor = stack.PopAddress(); if (inheritor is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, inheritor, false)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, inheritor, chargeForWarm: false)) return EvmExceptionType.OutOfGas; Address executingAccount = vmState.Env.ExecutingAccount; bool createInSameTx = vmState.AccessTracker.CreateList.Contains(executingAccount); From fac083213c463298065a467881ce85e8f0e0e411 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 03:32:11 +0000 Subject: [PATCH 186/255] Fix tracing --- .../Instructions/EvmInstructions.Call.cs | 29 ++++++++++++------- .../Instructions/EvmInstructions.Create.cs | 2 +- .../Instructions/EvmInstructions.Eof.cs | 2 +- .../Nethermind.Evm/VirtualMachine.cs | 2 +- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index f94f23d89f9..23e7235df20 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -106,36 +106,43 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E if (!transferValue.IsZero) { - //if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportExtraGasPressure(GasCostOf.CallStipend); + if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportExtraGasPressure(GasCostOf.CallStipend); gasLimitUl += GasCostOf.CallStipend; } + bool tracingInstructions = vm.TxTracer.IsTracingInstructions; if (env.CallDepth >= MaxCallDepth || !transferValue.IsZero && state.GetBalance(env.ExecutingAccount) < transferValue) { vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); - //if (typeof(TTracingInstructions) == typeof(IsTracing)) - //{ - // // very specific for Parity trace, need to find generalization - very peculiar 32 length... - // ReadOnlyMemory? memoryTrace = vm.State.Memory.Inspect(in dataOffset, 32); - // _txTracer.ReportMemoryChange(dataOffset, memoryTrace is null ? ReadOnlySpan.Empty : memoryTrace.Value.Span); - //} + if (vm.TxTracer.IsTracingRefunds) + { + // very specific for Parity trace, need to find generalization - very peculiar 32 length... + ReadOnlyMemory? memoryTrace = vm.EvmState.Memory.Inspect(in dataOffset, 32); + vm.TxTracer.ReportMemoryChange(dataOffset, memoryTrace is null ? ReadOnlySpan.Empty : memoryTrace.Value.Span); + } //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); - //if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationRemainingGas(gasAvailable); - //if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); + if (tracingInstructions) + { + vm.TxTracer.ReportOperationRemainingGas(gasAvailable); + vm.TxTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); + } UpdateGasUp(gasLimitUl, ref gasAvailable); - //if (typeof(TTracingInstructions) == typeof(IsTracing)) _txTracer.ReportGasUpdateForVmTrace(gasLimitUl, gasAvailable); + if (tracingInstructions) + { + vm.TxTracer.ReportGasUpdateForVmTrace(gasLimitUl, gasAvailable); + } return EvmExceptionType.None; } Snapshot snapshot = state.TakeSnapshot(); state.SubtractFromBalance(caller, transferValue, spec); - if (codeInfo.IsEmpty /*&& typeof(TTracingInstructions) != typeof(IsTracing) && !_txTracer.IsTracingActions*/) + if (codeInfo.IsEmpty && !tracingInstructions && !vm.TxTracer.IsTracingActions) { // Non contract call, no need to construct call frame can just credit balance and return gas vm.ReturnDataBuffer = default; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 59f1d7c7105..10fbb7f6ca5 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -96,7 +96,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r return EvmExceptionType.None; } - //if (vm.TxTracer.IsTracingInstructions) EndInstructionTrace(gasAvailable, vmState.Memory.Size); + if (vm.TxTracer.IsTracingInstructions) vm.EndInstructionTrace(gasAvailable); // todo: === below is a new call - refactor / move long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 06cb83dfad0..d8ee6377003 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -415,7 +415,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt } - // if (vm.TxTracer.IsTracingInstructions) EndInstructionTrace(gasAvailable, vm.State?.Memory.Size ?? 0); + if (vm.TxTracer.IsTracingInstructions) vm.EndInstructionTrace(gasAvailable); // todo: === below is a new call - refactor / move Snapshot snapshot = state.TakeSnapshot(); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index ecff746015c..d6f3c879851 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -802,7 +802,7 @@ private void StartInstructionTrace(Instruction instruction, long gasAvailable, i } [MethodImpl(MethodImplOptions.NoInlining)] - private void EndInstructionTrace(long gasAvailable) + internal void EndInstructionTrace(long gasAvailable) { _txTracer.ReportOperationRemainingGas(gasAvailable); } From 60ebed107ace37987cbd6a9a02dce6986c3d5017 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 04:53:21 +0000 Subject: [PATCH 187/255] Fix gas --- .../Instructions/EvmInstructions.Call.cs | 4 ++-- .../Instructions/EvmInstructions.Environment.cs | 13 +++++++++++++ .../Instructions/EvmInstructions.Eof.cs | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 23e7235df20..7d1337ccfa5 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -36,7 +36,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E Address codeSource = stack.PopAddress(); if (codeSource is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; UInt256 callValue; if (typeof(TOpCall) == typeof(OpStaticCall)) @@ -121,7 +121,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E { // very specific for Parity trace, need to find generalization - very peculiar 32 length... ReadOnlyMemory? memoryTrace = vm.EvmState.Memory.Inspect(in dataOffset, 32); - vm.TxTracer.ReportMemoryChange(dataOffset, memoryTrace is null ? ReadOnlySpan.Empty : memoryTrace.Value.Span); + vm.TxTracer.ReportMemoryChange(dataOffset, memoryTrace is null ? default : memoryTrace.Value.Span); } //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index 27f56b4c591..2883d2f05bc 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -164,6 +164,19 @@ public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref Evm return EvmExceptionType.None; } + private static bool ChargeAccountAccessGasWithDelegation(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) + { + IReleaseSpec spec = vm.Spec; + if (!spec.UseHotAndColdStorage) + { + return true; + } + bool notOutOfGas = ChargeAccountAccessGas(ref gasAvailable, vm, address, chargeForWarm); + return notOutOfGas + && (!vm.EvmState.Env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(vm.WorldState, address, spec, out Address delegated) + || ChargeAccountAccessGas(ref gasAvailable, vm, delegated, chargeForWarm)); + } + public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) { bool result = true; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index d8ee6377003..d6872a6dc3f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -609,7 +609,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, in dataLength)) return EvmExceptionType.OutOfGas; // 1. Charge WARM_STORAGE_READ_COST (100) gas. // 6. If target_address is not in the warm_account_list, charge COLD_ACCOUNT_ACCESS - WARM_STORAGE_READ_COST (2500) gas. - if (!ChargeAccountAccessGas(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; if ((!spec.ClearEmptyAccountWhenTouched && !state.AccountExists(codeSource)) || (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && state.IsDeadAccount(codeSource))) From 7f6e2394f4f993be92b4cff80ea2fbd92d98090f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 05:08:04 +0000 Subject: [PATCH 188/255] Fix benchmarks --- .../Nethermind.Evm.Benchmark/EvmBenchmarks.cs | 2 +- .../Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs | 12 ++++++------ .../MultipleUnsignedOperations.cs | 2 +- .../Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index 848682238c6..b4a5788f78a 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -64,7 +64,7 @@ public void GlobalSetup() [Benchmark] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.Run(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } } diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs index 4ea3983493b..313dd41a216 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs @@ -30,7 +30,7 @@ public void GlobalSetup() [ArgumentsSource(nameof(ValueSource))] public UInt256 Uint256(UInt256 v) { - EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); + EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); stack.PushUInt256(in v); stack.PopUInt256(out UInt256 value); @@ -50,7 +50,7 @@ public UInt256 Uint256(UInt256 v) [Benchmark(OperationsPerInvoke = 4)] public byte Byte() { - EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); + EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); byte b = 1; @@ -72,7 +72,7 @@ public byte Byte() [Benchmark(OperationsPerInvoke = 4)] public void PushZero() { - EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); + EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); stack.PushZero(); stack.PushZero(); @@ -83,7 +83,7 @@ public void PushZero() [Benchmark(OperationsPerInvoke = 4)] public void PushOne() { - EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); + EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); stack.PushOne(); stack.PushOne(); @@ -94,7 +94,7 @@ public void PushOne() [Benchmark(OperationsPerInvoke = 4)] public void Swap() { - EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); + EvmStack stack = new(0, NullTxTracer.Instance, _stack.AsSpan()); stack.Swap(2); stack.Swap(2); @@ -105,7 +105,7 @@ public void Swap() [Benchmark(OperationsPerInvoke = 4)] public void Dup() { - EvmStack stack = new(1, NullTxTracer.Instance, _stack.AsSpan()); + EvmStack stack = new(1, NullTxTracer.Instance, _stack.AsSpan()); stack.Dup(1); stack.Dup(1); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs index 788fd177b00..185632fb924 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs @@ -97,7 +97,7 @@ public void GlobalSetup() [Benchmark] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.Run(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs index eb3433f9535..bce0f7b4800 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs @@ -108,14 +108,14 @@ public void GlobalSetup() [Benchmark(Baseline = true)] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.Run(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } [Benchmark] public void ExecuteCodeNoTracing() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.Run(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } From e5632745b329f29a3a31306fc9c29caa865e891d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 05:41:02 +0000 Subject: [PATCH 189/255] Add EofCall tracing --- .../Instructions/EvmInstructions.Eof.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index d6872a6dc3f..b8f7f51657f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -9,13 +9,13 @@ using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.EvmObjectFormat.Handlers; +using Nethermind.Evm.Tracing; using Nethermind.State; +using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm; using Int256; -using static Nethermind.Evm.VirtualMachine; - internal sealed partial class EvmInstructions { [SkipLocalsInit] @@ -635,15 +635,16 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, stack.PushOne(); //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); - //if (_txTracer.IsTracingInstructions) - //{ - // // very specific for Parity trace, need to find generalization - very peculiar 32 length... - // ReadOnlyMemory memoryTrace = vmState.Memory.Inspect(in dataOffset, 32); - // _txTracer.ReportMemoryChange(dataOffset, memoryTrace.Span); - // _txTracer.ReportOperationRemainingGas(gasAvailable); - // _txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); - // _txTracer.ReportGasUpdateForVmTrace(callGas, gasAvailable); - //} + ITxTracer txTracer = vm.TxTracer; + if (txTracer.IsTracingInstructions) + { + // very specific for Parity trace, need to find generalization - very peculiar 32 length... + ReadOnlyMemory memoryTrace = vm.EvmState.Memory.Inspect(in dataOffset, 32); + txTracer.ReportMemoryChange(dataOffset, memoryTrace.Span); + txTracer.ReportOperationRemainingGas(gasAvailable); + txTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); + txTracer.ReportGasUpdateForVmTrace(callGas, gasAvailable); + } return EvmExceptionType.None; } From c1fcddff381f1b2fb403fbf1b062c980510650b7 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 05:53:02 +0000 Subject: [PATCH 190/255] InstructionReturnDataCopy --- .../Nethermind.Evm/Instructions/EvmInstructions.Eof.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index b8f7f51657f..030322cdc90 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -32,10 +32,11 @@ public static EvmExceptionType InstructionReturnDataSize(VirtualMachine vm, ref [SkipLocalsInit] public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 c)) return EvmExceptionType.StackUnderflow; - gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in c); + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) + return EvmExceptionType.StackUnderflow; + + gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in c, out bool outOfGas); + if (outOfGas) return EvmExceptionType.OutOfGas; ReadOnlyMemory returnDataBuffer = vm.ReturnDataBuffer; if (vm.EvmState.Env.CodeInfo.Version == 0 && (UInt256.AddOverflow(c, b, out UInt256 result) || result > returnDataBuffer.Length)) From 98462948b729b6788f857d771aaa1edb8fe62fd4 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 16:53:50 +0000 Subject: [PATCH 191/255] Fix static --- .../Nethermind.Evm/Instructions/EvmInstructions.Call.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 7d1337ccfa5..348fa8cda4e 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -58,7 +58,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E if (!stack.PopUInt256(out UInt256 outputOffset)) return EvmExceptionType.StackUnderflow; if (!stack.PopUInt256(out UInt256 outputLength)) return EvmExceptionType.StackUnderflow; - if (vm.EvmState.IsStatic && !transferValue.IsZero && typeof(TOpCall) == typeof(OpCallCode)) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic && !transferValue.IsZero && typeof(TOpCall) != typeof(OpCallCode)) return EvmExceptionType.StaticCallViolation; Address caller = typeof(TOpCall) == typeof(OpDelegateCall) ? env.Caller : env.ExecutingAccount; Address target = typeof(TOpCall) == typeof(OpCall) || typeof(TOpCall) == typeof(OpStaticCall) From 3a512c770e660494d16db59ae6d447b6f22ad481 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 17:21:02 +0000 Subject: [PATCH 192/255] Tidy up --- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 1 - .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 92 +++++++++---------- .../Nethermind.Evm/CodeInfoFactory.cs | 15 ++- .../Nethermind.Evm/CodeInfoRepository.cs | 3 +- .../EvmObjectFormat/EofCodeInfo.cs | 9 +- .../EvmObjectFormat/EofCodeValidator.cs | 4 +- .../EvmObjectFormat/EofHeader.cs | 6 +- .../EvmObjectFormat/Handlers/EofV1.cs | 6 +- src/Nethermind/Nethermind.Evm/EvmState.cs | 2 - src/Nethermind/Nethermind.Evm/Instruction.cs | 5 +- .../Instructions/EvmInstructions.Call.cs | 46 +++++----- .../Instructions/EvmInstructions.Eof.cs | 19 ++-- src/Nethermind/Nethermind.Evm/StackPool.cs | 2 - .../GethStyle/GethLikeBlockFileTracer.cs | 2 + .../TransactionProcessor.cs | 5 +- 15 files changed, 102 insertions(+), 115 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index 1d0ed1fc073..54758ff59f3 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 027190c75a0..7f748efe405 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -2,68 +2,66 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Diagnostics; using System.Threading; - using Nethermind.Evm.Precompiles; -using System.Diagnostics; using Nethermind.Evm.EvmObjectFormat; -namespace Nethermind.Evm.CodeAnalysis +namespace Nethermind.Evm.CodeAnalysis; + +public class CodeInfo : ICodeInfo, IThreadPoolWorkItem { - public class CodeInfo : ICodeInfo, IThreadPoolWorkItem - { - public ReadOnlyMemory MachineCode { get; } - public IPrecompile? Precompile { get; set; } - private readonly JumpDestinationAnalyzer _analyzer; - private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); - public static CodeInfo Empty { get; } = new CodeInfo([]); + public ReadOnlyMemory MachineCode { get; } + public IPrecompile? Precompile { get; set; } + private readonly JumpDestinationAnalyzer _analyzer; + private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); + public static CodeInfo Empty { get; } = new CodeInfo([]); - public CodeInfo(byte[] code) - { - MachineCode = code; - _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); - } + public CodeInfo(byte[] code) + { + MachineCode = code; + _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); + } - public CodeInfo(ReadOnlyMemory code) - { - MachineCode = code; - _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); - } + public CodeInfo(ReadOnlyMemory code) + { + MachineCode = code; + _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); + } - public bool IsPrecompile => Precompile is not null; - public bool IsEmpty => ReferenceEquals(_analyzer, _emptyAnalyzer) && !IsPrecompile; + public bool IsPrecompile => Precompile is not null; + public bool IsEmpty => ReferenceEquals(_analyzer, _emptyAnalyzer) && !IsPrecompile; - public CodeInfo(IPrecompile precompile) - { - Precompile = precompile; - MachineCode = Array.Empty(); - _analyzer = _emptyAnalyzer; - } + public CodeInfo(IPrecompile precompile) + { + Precompile = precompile; + MachineCode = Array.Empty(); + _analyzer = _emptyAnalyzer; + } - public bool ValidateJump(int destination) - { - return _analyzer.ValidateJump(destination); - } + public bool ValidateJump(int destination) + { + return _analyzer.ValidateJump(destination); + } - void IThreadPoolWorkItem.Execute() - { - _analyzer.Execute(); - } + void IThreadPoolWorkItem.Execute() + { + _analyzer.Execute(); + } - public void AnalyseInBackgroundIfRequired() + public void AnalyseInBackgroundIfRequired() + { + if (!ReferenceEquals(_analyzer, _emptyAnalyzer) && _analyzer.RequiresAnalysis) { - if (!ReferenceEquals(_analyzer, _emptyAnalyzer) && _analyzer.RequiresAnalysis) - { - ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false); - } + ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false); } + } - public SectionHeader CodeSectionOffset(int idx) - => throw new UnreachableException(); + public SectionHeader CodeSectionOffset(int idx) + => throw new UnreachableException(); - public SectionHeader? ContainerSectionOffset(int idx) - => throw new UnreachableException(); + public SectionHeader? ContainerSectionOffset(int idx) + => throw new UnreachableException(); - public int PcOffset() => 0; - } + public int PcOffset() => 0; } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index aaa8d0066ca..68c8bf511b1 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -1,17 +1,17 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Nethermind.Core.Specs; using Nethermind.Evm.EvmObjectFormat; -using System; namespace Nethermind.Evm.CodeAnalysis; public static class CodeInfoFactory { - public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, EvmObjectFormat.ValidationStrategy validationRules = EvmObjectFormat.ValidationStrategy.ExractHeader) + public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec spec, ValidationStrategy validationRules = ValidationStrategy.ExractHeader) { - CodeInfo codeInfo = new CodeInfo(code); + CodeInfo codeInfo = new(code); if (spec.IsEofEnabled && code.Span.StartsWith(EofValidator.MAGIC)) { if (EofValidator.IsValidEof(code, validationRules, out EofContainer? container)) @@ -23,16 +23,15 @@ public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec s return codeInfo; } - public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out ICodeInfo codeInfo, out Memory extraCalldata) + public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out ICodeInfo codeInfo, out Memory extraCallData) { - extraCalldata = default; + extraCallData = default; if (spec.IsEofEnabled && data.Span.StartsWith(EofValidator.MAGIC)) { - if (EofValidator.IsValidEof(data, EvmObjectFormat.ValidationStrategy.ValidateInitcodeMode | EvmObjectFormat.ValidationStrategy.ValidateFullBody | EvmObjectFormat.ValidationStrategy.AllowTrailingBytes, out EofContainer? eofContainer)) + if (EofValidator.IsValidEof(data, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateFullBody | ValidationStrategy.AllowTrailingBytes, out EofContainer? eofContainer)) { int containerSize = eofContainer.Value.Header.DataSection.EndOffset; - extraCalldata = data.Slice(containerSize); - ICodeInfo innerCodeInfo = new CodeInfo(data.Slice(0, containerSize)); + extraCallData = data[containerSize..]; codeInfo = new EofCodeInfo(eofContainer.Value); return true; } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index aa18ad474fd..fe05954fd1c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -17,7 +17,6 @@ using Nethermind.Evm.Precompiles.Snarks; using Nethermind.State; using Nethermind.Evm.EvmObjectFormat; -using Nethermind.Crypto; namespace Nethermind.Evm; @@ -185,7 +184,7 @@ private static bool TryGetDelegatedAddress(ReadOnlySpan code, [NotNullWhen return false; } - private ICodeInfo CreateCachedPrecompile( + private static ICodeInfo CreateCachedPrecompile( in KeyValuePair originalPrecompile, ConcurrentDictionary cache) => new CodeInfo(new CachedPrecompile(originalPrecompile.Key.Value, originalPrecompile.Value.Precompile!, cache)); diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index 49546abcf2b..7adef13124e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -9,9 +9,9 @@ namespace Nethermind.Evm.CodeAnalysis; -public class EofCodeInfo : ICodeInfo +public class EofCodeInfo(in EofContainer container) : ICodeInfo { - public EofContainer EofContainer { get; private set; } + public EofContainer EofContainer { get; private set; } = container; public ReadOnlyMemory MachineCode => EofContainer.Container; public IPrecompile? Precompile => null; public int Version => EofContainer.Header.Version; @@ -37,9 +37,4 @@ public class EofCodeInfo : ICodeInfo } public bool ValidateJump(int destination) => true; - - public EofCodeInfo(in EofContainer container) - { - EofContainer = container; - } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index b816fb940da..a6e1b06874e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -16,12 +16,12 @@ 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 byte[] MAGIC = { 0xEF, 0x00 }; + public static byte[] 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 lenght - private static readonly Dictionary _eofVersionHandlers = new(); + private static readonly Dictionary _eofVersionHandlers = []; internal static ILogger Logger { get; set; } = NullLogger.Instance; static EofValidator() diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs index 4211222949d..4471e6d6beb 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofHeader.cs @@ -16,7 +16,7 @@ public EofContainer(ReadOnlyMemory container, EofHeader eofHeader) { Container = container; Header = eofHeader; - Prefix = container.Slice(0, eofHeader.PrefixSize); + 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; @@ -46,7 +46,7 @@ public EofContainer(ReadOnlyMemory container, EofHeader eofHeader) ContainerSections = Array.Empty>(); } - DataSection = container.Slice(eofHeader.DataSection.Start); + DataSection = container[eofHeader.DataSection.Start..]; } public readonly EofHeader Header; @@ -106,7 +106,7 @@ private static int[] CreateSubSectionsSizes(int[] subSectionsSizes) private int[] SubSectionsSizesAcc { get; } = CreateSubSectionsSizes(subSectionsSizes); - public SectionHeader this[int i] => new SectionHeader(SubSectionsSizesAcc[i], (ushort)SubSectionsSizes[i]); + 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/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index dde44a1078e..a1143910209 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -57,12 +57,12 @@ public void Combine(StackBounds other) this.Min = Math.Min(this.Min, other.Min); } - public bool BoundsEqual() => Max == Min; + public readonly bool BoundsEqual() => Max == Min; public static bool operator ==(StackBounds left, StackBounds right) => left.Max == right.Max && right.Min == left.Min; public static bool operator !=(StackBounds left, StackBounds right) => !(left == right); - public override bool Equals(object obj) => obj is StackBounds && this == (StackBounds)obj; - public override int GetHashCode() => Max ^ Min; + public override readonly bool Equals(object obj) => obj is StackBounds bounds && this == bounds; + public override readonly int GetHashCode() => Max ^ Min; } private ref struct Sizes diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index 7a00f07c306..eb56c021e12 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using Nethermind.Core; using Nethermind.State; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 1a86013a006..9fd88f4991d 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -1,12 +1,11 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using System.Diagnostics.CodeAnalysis; using FastEnumUtility; using Nethermind.Core.Specs; -using Nethermind.Evm.EvmObjectFormat; using Nethermind.Specs.Forks; -using System; -using System.Diagnostics.CodeAnalysis; using static Nethermind.Evm.EvmObjectFormat.EofValidator; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 348fa8cda4e..06287f7a02f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -32,11 +32,11 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; IWorldState state = vm.WorldState; - if (!stack.PopUInt256(out UInt256 gasLimit)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 gasLimit)) goto StackUnderflow; Address codeSource = stack.PopAddress(); - if (codeSource is null) return EvmExceptionType.StackUnderflow; + if (codeSource is null) goto StackUnderflow; - if (!ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) goto OutOfGas; UInt256 callValue; if (typeof(TOpCall) == typeof(OpStaticCall)) @@ -49,14 +49,14 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E } else if (!stack.PopUInt256(out callValue)) { - return EvmExceptionType.StackUnderflow; + goto StackUnderflow; } UInt256 transferValue = typeof(TOpCall) == typeof(OpDelegateCall) ? UInt256.Zero : callValue; - if (!stack.PopUInt256(out UInt256 dataOffset)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 dataLength)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 outputOffset)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 outputLength)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 dataOffset) || + !stack.PopUInt256(out UInt256 dataLength) || + !stack.PopUInt256(out UInt256 outputOffset) || + !stack.PopUInt256(out UInt256 outputLength)) goto StackUnderflow; if (vm.EvmState.IsStatic && !transferValue.IsZero && typeof(TOpCall) != typeof(OpCallCode)) return EvmExceptionType.StaticCallViolation; @@ -89,7 +89,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E if (!UpdateGas(spec.GetCallCost(), ref gasAvailable) || !UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataLength) || !UpdateMemoryCost(vm.EvmState, ref gasAvailable, in outputOffset, outputLength) || - !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; + !UpdateGas(gasExtra, ref gasAvailable)) goto OutOfGas; ICodeInfo codeInfo = vm.CodeInfoRepository.GetCachedCodeInfo(state, codeSource, spec); codeInfo.AnalyseInBackgroundIfRequired(); @@ -99,10 +99,10 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E gasLimit = UInt256.Min((UInt256)(gasAvailable - gasAvailable / 64), gasLimit); } - if (gasLimit >= long.MaxValue) return EvmExceptionType.OutOfGas; + if (gasLimit >= long.MaxValue) goto OutOfGas; long gasLimitUl = (long)gasLimit; - if (!UpdateGas(gasLimitUl, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(gasLimitUl, ref gasAvailable)) goto OutOfGas; if (!transferValue.IsZero) { @@ -184,7 +184,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E return EvmExceptionType.None; - EvmExceptionType FastCall(VirtualMachine vm, IReleaseSpec spec, in UInt256 transferValue, Address target) + static EvmExceptionType FastCall(VirtualMachine vm, IReleaseSpec spec, in UInt256 transferValue, Address target) { IWorldState state = vm.WorldState; state.AddToBalanceAndCreateIfNotExists(target, transferValue, spec); @@ -194,15 +194,19 @@ EvmExceptionType FastCall(VirtualMachine vm, IReleaseSpec spec, in UInt256 trans return EvmExceptionType.None; } - //[MethodImpl(MethodImplOptions.NoInlining)] - //void TraceCallDetails(Address codeSource, ref UInt256 callValue, ref UInt256 transferValue, Address caller, Address target) - //{ - // _logger.Trace($"caller {caller}"); - // _logger.Trace($"code source {codeSource}"); - // _logger.Trace($"target {target}"); - // _logger.Trace($"value {callValue}"); - // _logger.Trace($"transfer value {transferValue}"); - //} + //[MethodImpl(MethodImplOptions.NoInlining)] + //void TraceCallDetails(Address codeSource, ref UInt256 callValue, ref UInt256 transferValue, Address caller, Address target) + //{ + // _logger.Trace($"caller {caller}"); + // _logger.Trace($"code source {codeSource}"); + // _logger.Trace($"target {target}"); + // _logger.Trace($"value {callValue}"); + // _logger.Trace($"transfer value {transferValue}"); + //} + StackUnderflow: + return EvmExceptionType.StackUnderflow; + OutOfGas: + return EvmExceptionType.OutOfGas; } public struct OpCall : IOpCall diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 030322cdc90..5d2d5b15165 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -240,8 +240,6 @@ public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref if (!UpdateGas(GasCostOf.Retf, ref gasAvailable)) return EvmExceptionType.OutOfGas; - (_, int outputCount, _) = codeInfo.GetSectionMetadata(vm.SectionIndex); - EvmState.ReturnState stackFrame = vm.EvmState.ReturnStack[--vm.EvmState.ReturnStackHead]; vm.SectionIndex = stackFrame.Index; programCounter = stackFrame.Offset; @@ -440,7 +438,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt state.SubtractFromBalance(env.ExecutingAccount, value, spec); - ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initContainer.ToArray(), spec, EvmObjectFormat.ValidationStrategy.ExractHeader); + ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initContainer.ToArray(), spec, ValidationStrategy.ExractHeader); ExecutionEnvironment callEnv = new ( @@ -483,22 +481,21 @@ public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref byte sectionIdx = codeInfo.CodeSection.Span[programCounter++]; ReadOnlyMemory deployCode = codeInfo.ContainerSection[(Range)codeInfo.ContainerSectionOffset(sectionIdx)]; - EofCodeInfo deploycodeInfo = (EofCodeInfo)CodeInfoFactory.CreateCodeInfo(deployCode, spec, EvmObjectFormat.ValidationStrategy.ExractHeader); + EofCodeInfo deployCodeInfo = (EofCodeInfo)CodeInfoFactory.CreateCodeInfo(deployCode, spec, ValidationStrategy.ExractHeader); stack.PopUInt256(out UInt256 a); stack.PopUInt256(out UInt256 b); - ReadOnlyMemory auxData = ReadOnlyMemory.Empty; if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; - int projectedNewSize = (int)b + deploycodeInfo.DataSection.Length; - if (projectedNewSize < deploycodeInfo.EofContainer.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) + int projectedNewSize = (int)b + deployCodeInfo.DataSection.Length; + if (projectedNewSize < deployCodeInfo.EofContainer.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) { return EvmExceptionType.AccessViolation; } vm.ReturnDataBuffer = vm.EvmState.Memory.Load(a, b); - vm.ReturnData = deploycodeInfo; + vm.ReturnData = deployCodeInfo; return EvmExceptionType.None; } @@ -529,18 +526,18 @@ public interface IOpEofCall public struct OpEofCall : IOpEofCall { - public static ExecutionType ExecutionType => Evm.ExecutionType.EOFCALL; + public static ExecutionType ExecutionType => ExecutionType.EOFCALL; } public struct OpEofDelegateCall : IOpEofCall { - public static ExecutionType ExecutionType => Evm.ExecutionType.EOFDELEGATECALL; + public static ExecutionType ExecutionType => ExecutionType.EOFDELEGATECALL; } public struct OpEofStaticCall : IOpEofCall { public static bool IsStatic => true; - public static ExecutionType ExecutionType => Evm.ExecutionType.EOFSTATICCALL; + public static ExecutionType ExecutionType => ExecutionType.EOFSTATICCALL; } [SkipLocalsInit] diff --git a/src/Nethermind/Nethermind.Evm/StackPool.cs b/src/Nethermind/Nethermind.Evm/StackPool.cs index 7d2b9855612..1564e8c4c86 100644 --- a/src/Nethermind/Nethermind.Evm/StackPool.cs +++ b/src/Nethermind/Nethermind.Evm/StackPool.cs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics; using static Nethermind.Evm.EvmState; diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockFileTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockFileTracer.cs index 15d0166e0c0..4bc26040d30 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockFileTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeBlockFileTracer.cs @@ -103,8 +103,10 @@ private string GetFileName(Hash256 txHash) }); for (; index < _block.Transactions.Length; index++) + { if (_block.Transactions[index].Hash == txHash) break; + } return string.Format(_fileNameFormat, index, hash, suffix); } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 4ef56ee258c..2f0897bf386 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -547,8 +547,7 @@ private TransactionResult BuildExecutionEnvironment( if (recipient is null) ThrowInvalidDataException("Recipient has not been resolved properly before tx execution"); TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, codeInfoRepository); - Address? delegationAddress = null; - ICodeInfo? codeInfo = null; + ICodeInfo? codeInfo; ReadOnlyMemory inputData = tx.IsMessageCall ? tx.Data ?? default : default; if (tx.IsContractCreation) { @@ -559,7 +558,7 @@ private TransactionResult BuildExecutionEnvironment( } else { - codeInfo = codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec, out delegationAddress); + codeInfo = codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec, out Address? delegationAddress); //We assume eip-7702 must be active if it is a delegation if (delegationAddress is not null) From a5dde38c49b02a9ef61d0b1f1f55f036786f9d15 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 19:31:08 +0000 Subject: [PATCH 193/255] Optimize --- .../Instructions/EvmInstructions.Bitwise.cs | 7 +- .../Instructions/EvmInstructions.Call.cs | 15 +- .../Instructions/EvmInstructions.CodeCopy.cs | 31 ++- .../Instructions/EvmInstructions.Create.cs | 32 ++- .../EvmInstructions.Environment.cs | 56 +++-- .../Instructions/EvmInstructions.Eof.cs | 236 +++++++++++++----- .../Instructions/EvmInstructions.Extras.cs | 18 +- .../Instructions/EvmInstructions.Jump.cs | 20 +- .../EvmInstructions.Math1Param.cs | 5 +- .../EvmInstructions.Math2Param.cs | 6 +- .../EvmInstructions.Math3Param.cs | 7 +- .../Instructions/EvmInstructions.Shifts.cs | 15 +- .../Instructions/EvmInstructions.Stack.cs | 26 +- .../Instructions/EvmInstructions.Storage.cs | 69 +++-- .../Instructions/EvmInstructions.cs | 16 +- src/Nethermind/Nethermind.Evm/Metrics.cs | 3 - .../Nethermind.Evm/VirtualMachine.cs | 21 +- 17 files changed, 401 insertions(+), 182 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs index e29c4f3c291..369097c99f0 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs @@ -22,16 +22,19 @@ public static EvmExceptionType InstructionBitwise(VirtualMachine _, gasAvailable -= TOpBitwise.GasCost; ref byte bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) return EvmExceptionType.StackUnderflow; + if (IsNullRef(ref bytesRef)) goto StackUnderflow; Vector256 aVec = ReadUnaligned>(ref bytesRef); bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) return EvmExceptionType.StackUnderflow; + if (IsNullRef(ref bytesRef)) goto StackUnderflow; Vector256 bVec = ReadUnaligned>(ref bytesRef); WriteUnaligned(ref stack.PushBytesRef(), TOpBitwise.Operation(aVec, bVec)); return EvmExceptionType.None; + // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } public struct OpBitwiseAnd : IOpBitwise diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 06287f7a02f..b6e07593af1 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -235,22 +235,27 @@ public static EvmExceptionType InstructionReturn(VirtualMachine vm, ref EvmStack { if (vm.EvmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) { - return EvmExceptionType.BadInstruction; + goto BadInstruction; } if (!stack.PopUInt256(out UInt256 position) || !stack.PopUInt256(out UInt256 length)) - return EvmExceptionType.StackUnderflow; + goto StackUnderflow; if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in position, in length)) { - return EvmExceptionType.OutOfGas; + goto OutOfGas; } vm.ReturnData = vm.EvmState.Memory.Load(in position, in length).ToArray(); return EvmExceptionType.None; - - + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + BadInstruction: + return EvmExceptionType.BadInstruction; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 25a47dca1a1..9ad317d5635 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -21,14 +21,13 @@ public interface IOpCodeCopy public static EvmExceptionType InstructionCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCodeCopy : struct, IOpCodeCopy { - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result); + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); + if (outOfGas) goto OutOfGas; if (!result.IsZero) { - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) goto OutOfGas; ZeroPaddedSpan slice = TOpCodeCopy.GetCode(vm).SliceWithZeroPadding(in b, (int)result); vm.EvmState.Memory.Save(in a, in slice); if (vm.TxTracer.IsTracingInstructions) @@ -38,6 +37,11 @@ public static EvmExceptionType InstructionCodeCopy(VirtualMachine v } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } public struct OpCallDataCopy : IOpCodeCopy @@ -57,10 +61,7 @@ public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref Evm { IReleaseSpec spec = vm.Spec; Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; - if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) goto StackUnderflow; - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + if (address is null || !stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 result)) goto StackUnderflow; gasAvailable -= spec.GetExtCodeCost() + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); if (outOfGas) goto OutOfGas; @@ -85,6 +86,7 @@ public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref Evm } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: @@ -98,9 +100,9 @@ public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref Evm gasAvailable -= spec.GetExtCodeCost(); Address address = stack.PopAddress(); - if (address is null) return EvmExceptionType.StackUnderflow; + if (address is null) goto StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; ReadOnlySpan codeSection = vm.EvmState.Env.CodeInfo.MachineCode.Span; if (!vm.TxTracer.IsTracingInstructions && programCounter < codeSection.Length) @@ -117,7 +119,7 @@ public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref Evm stack.PeekUInt256IsZero()) { optimizeAccess = true; - if (!stack.PopLimbo()) return EvmExceptionType.StackUnderflow; + if (!stack.PopLimbo()) goto StackUnderflow; } if (optimizeAccess) @@ -163,5 +165,10 @@ public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref Evm stack.PushUInt256(in result); } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 10fbb7f6ca5..dcf834927d3 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Diagnostics; using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Specs; @@ -29,7 +28,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r Metrics.IncrementCreates(); IReleaseSpec spec = vm.Spec; - if (vm.EvmState.IsStatic) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic) goto StaticCallViolation; vm.ReturnData = null; ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; @@ -44,7 +43,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r if (!stack.PopUInt256(out UInt256 value) || !stack.PopUInt256(out UInt256 memoryPositionOfInitCode) || !stack.PopUInt256(out UInt256 initCodeLength)) - return EvmExceptionType.StackUnderflow; + goto StackUnderflow; Span salt = default; if (typeof(TOpCreate) == typeof(OpCreate2)) @@ -55,7 +54,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r //EIP-3860 if (spec.IsEip3860Enabled) { - if (initCodeLength > spec.MaxInitCodeSize) return EvmExceptionType.OutOfGas; + if (initCodeLength > spec.MaxInitCodeSize) goto OutOfGas; } long gasCost = GasCostOf.Create + @@ -64,9 +63,9 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r ? GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in initCodeLength) : 0); - if (!UpdateGas(gasCost, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(gasCost, ref gasAvailable)) goto OutOfGas; - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memoryPositionOfInitCode, in initCodeLength)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memoryPositionOfInitCode, in initCodeLength)) goto OutOfGas; // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients @@ -74,7 +73,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r // TODO: need a test for this vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); - return EvmExceptionType.None; + goto None; } ReadOnlyMemory initCode = vm.EvmState.Memory.Load(in memoryPositionOfInitCode, in initCodeLength); @@ -84,7 +83,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r { vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); - return EvmExceptionType.None; + goto None; } UInt256 accountNonce = state.GetNonce(env.ExecutingAccount); @@ -93,14 +92,14 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r { vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); - return EvmExceptionType.None; + goto None; } if (vm.TxTracer.IsTracingInstructions) vm.EndInstructionTrace(gasAvailable); // todo: === below is a new call - refactor / move long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; - if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(callGas, ref gasAvailable)) goto OutOfGas; Address contractAddress = typeof(TOpCreate) == typeof(OpCreate) ? ContractAddress.From(env.ExecutingAccount, state.GetNonce(env.ExecutingAccount)) @@ -120,7 +119,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); UpdateGasUp(callGas, ref gasAvailable); - return EvmExceptionType.None; + goto None; } state.IncrementNonce(env.ExecutingAccount); @@ -138,7 +137,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); - return EvmExceptionType.None; + goto None; } if (state.IsDeadAccount(contractAddress)) @@ -171,8 +170,15 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r env: in callEnv, in vm.EvmState.AccessTracker ); - + None: return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + StaticCallViolation: + return EvmExceptionType.StaticCallViolation; } public struct OpCreate : IOpCreate diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index 2883d2f05bc..c986182a7db 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -41,14 +41,19 @@ public static EvmExceptionType InstructionBalance(VirtualMachine vm, ref EvmStac gasAvailable -= spec.GetBalanceCost(); Address address = stack.PopAddress(); - if (address is null) return EvmExceptionType.StackUnderflow; + if (address is null) goto StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; UInt256 result = vm.WorldState.GetBalance(address); stack.PushUInt256(in result); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -58,8 +63,8 @@ public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref Evm gasAvailable -= spec.GetExtCodeHashCost(); Address address = stack.PopAddress(); - if (address is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + if (address is null) goto StackUnderflow; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; IWorldState state = vm.WorldState; if (state.IsDeadAccount(address)) @@ -72,6 +77,11 @@ public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref Evm } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -81,8 +91,8 @@ public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref gasAvailable -= spec.GetExtCodeHashCost(); Address address = stack.PopAddress(); - if (address is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) return EvmExceptionType.OutOfGas; + if (address is null) goto StackUnderflow; + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; IWorldState state = vm.WorldState; if (state.IsDeadAccount(address)) @@ -103,6 +113,11 @@ public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -110,15 +125,20 @@ public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack { gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; EvmState vmState = vm.EvmState; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; Span bytes = vmState.Memory.LoadSpan(in result); if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange(result, bytes); stack.PushBytes(bytes); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -126,15 +146,20 @@ public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack { gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; Span bytes = stack.PopWord256(); EvmState vmState = vm.EvmState; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; vmState.Memory.SaveWord(in result, bytes); if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange((long)result, bytes); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -142,15 +167,20 @@ public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStac { gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; byte data = stack.PopByte(); EvmState vmState = vm.EvmState; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in UInt256.One)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in UInt256.One)) goto OutOfGas; vmState.Memory.SaveByte(in result, data); if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange(result, data); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -231,8 +261,6 @@ public static EvmExceptionType InstructionEnvBytes(VirtualMachine vm, re public static EvmExceptionType InstructionEnvUInt256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEnv : struct, IOpEnvUInt256 { - if (typeof(TOpEnv) == typeof(OpBaseFee) && !vm.Spec.BaseFeeEnabled) return EvmExceptionType.BadInstruction; - if (typeof(TOpEnv) == typeof(OpBlobBaseFee) && !vm.Spec.BlobBaseFeeEnabled) return EvmExceptionType.BadInstruction; gasAvailable -= TOpEnv.GasCost; TOpEnv.Operation(vm.EvmState, out UInt256 result); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 5d2d5b15165..0bdb60b6f1c 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -33,15 +33,15 @@ public static EvmExceptionType InstructionReturnDataSize(VirtualMachine vm, ref public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) - return EvmExceptionType.StackUnderflow; + goto StackUnderflow; gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in c, out bool outOfGas); - if (outOfGas) return EvmExceptionType.OutOfGas; + if (outOfGas) goto OutOfGas; ReadOnlyMemory returnDataBuffer = vm.ReturnDataBuffer; if (vm.EvmState.Env.CodeInfo.Version == 0 && (UInt256.AddOverflow(c, b, out UInt256 result) || result > returnDataBuffer.Length)) { - return EvmExceptionType.AccessViolation; + goto AccessViolation; } if (!c.IsZero) @@ -56,6 +56,13 @@ public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + AccessViolation: + return EvmExceptionType.AccessViolation; } [SkipLocalsInit] @@ -63,15 +70,20 @@ public static EvmExceptionType InstructionDataLoad(VirtualMachine vm, ref EvmSta { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.DataLoad, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.DataLoad, ref gasAvailable)) goto OutOfGas; stack.PopUInt256(out UInt256 a); ZeroPaddedSpan zpbytes = codeInfo.DataSection.SliceWithZeroPadding(a, 32); stack.PushBytes(zpbytes); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] @@ -79,9 +91,9 @@ public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmSt { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.DataLoadN, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.DataLoadN, ref gasAvailable)) goto OutOfGas; var offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); ZeroPaddedSpan zpbytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); @@ -90,6 +102,11 @@ public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmSt 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; } [SkipLocalsInit] @@ -97,13 +114,18 @@ public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmSta { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.DataSize, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.DataSize, ref gasAvailable)) goto OutOfGas; stack.PushUInt32(codeInfo.DataSection.Length); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] @@ -111,19 +133,17 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - stack.PopUInt256(out UInt256 memOffset); - stack.PopUInt256(out UInt256 offset); - stack.PopUInt256(out UInt256 size); + if (!stack.PopUInt256(out UInt256 memOffset) || stack.PopUInt256(out UInt256 offset) || stack.PopUInt256(out UInt256 size)) goto StackUnderflow; if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable)) - return EvmExceptionType.OutOfGas; + goto OutOfGas; if (!size.IsZero) { if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memOffset, size)) - return EvmExceptionType.OutOfGas; + goto OutOfGas; ZeroPaddedSpan dataSectionSlice = codeInfo.DataSection.SliceWithZeroPadding(offset, (int)size); vm.EvmState.Memory.Save(in memOffset, dataSectionSlice); if (vm.TxTracer.IsTracingInstructions) @@ -135,6 +155,13 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta stack.PushUInt32(codeInfo.DataSection.Length); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] @@ -142,14 +169,19 @@ public static EvmExceptionType InstructionRelativeJump(VirtualMachine vm, ref Ev { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) goto OutOfGas; 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; } [SkipLocalsInit] @@ -157,9 +189,9 @@ public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.RJumpi, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.RJumpi, ref gasAvailable)) goto OutOfGas; Span condition = stack.PopWord256(); short offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); @@ -170,6 +202,11 @@ public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref 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; } [SkipLocalsInit] @@ -177,9 +214,9 @@ public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmSt { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; stack.PopUInt256(out UInt256 a); ReadOnlySpan codeSection = codeInfo.CodeSection.Span; @@ -195,6 +232,11 @@ public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmSt programCounter += immediates; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] @@ -202,9 +244,9 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.Callf, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.Callf, ref gasAvailable)) goto OutOfGas; ReadOnlySpan codeSection = codeInfo.CodeSection.Span; var index = (int)codeSection.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); @@ -212,11 +254,11 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) { - return EvmExceptionType.StackOverflow; + goto StackOverflow; } if (vm.EvmState.ReturnStackHead == Eof1.RETURN_STACK_MAX_HEIGHT) - return EvmExceptionType.InvalidSubroutineEntry; + goto InvalidSubroutineEntry; vm.EvmState.ReturnStack[vm.EvmState.ReturnStackHead++] = new EvmState.ReturnState { @@ -229,6 +271,15 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev 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; } [SkipLocalsInit] @@ -236,15 +287,20 @@ public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.Retf, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.Retf, ref gasAvailable)) goto OutOfGas; EvmState.ReturnState stackFrame = vm.EvmState.ReturnStack[--vm.EvmState.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; } [SkipLocalsInit] @@ -252,21 +308,28 @@ public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref Ev { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.Jumpf, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.Jumpf, ref gasAvailable)) goto OutOfGas; int index = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); (int inputCount, _, int maxStackHeight) = codeInfo.GetSectionMetadata(index); if (Eof1.MAX_STACK_HEIGHT - maxStackHeight + inputCount < stack.Head) { - return EvmExceptionType.StackOverflow; + 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; } [SkipLocalsInit] @@ -274,9 +337,9 @@ public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack s { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) goto OutOfGas; int imm = codeInfo.CodeSection.Span[programCounter]; stack.Dup(imm + 1); @@ -284,6 +347,11 @@ public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack s programCounter += 1; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] @@ -291,16 +359,23 @@ public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) goto OutOfGas; int n = 1 + (int)codeInfo.CodeSection.Span[programCounter]; - if (!stack.Swap(n + 1)) return EvmExceptionType.StackUnderflow; + if (!stack.Swap(n + 1)) goto StackUnderflow; programCounter += 1; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] @@ -308,9 +383,9 @@ public static EvmExceptionType InstructionExchange(VirtualMachine vm, ref EvmSta { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) goto OutOfGas; ReadOnlySpan codeSection = codeInfo.CodeSection.Span; int n = 1 + (int)(codeSection[programCounter] >> 0x04); @@ -321,6 +396,11 @@ public static EvmExceptionType InstructionExchange(VirtualMachine vm, ref EvmSta programCounter += 1; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] @@ -332,9 +412,9 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt IReleaseSpec spec = vm.Spec; ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; - if (vm.EvmState.IsStatic) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic) goto StaticCallViolation; ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; EofCodeInfo container = env.CodeInfo as EofCodeInfo; @@ -342,7 +422,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt // 1 - deduct TX_CREATE_COST gas if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) - return EvmExceptionType.OutOfGas; + goto OutOfGas; ReadOnlySpan codeSection = codeInfo.CodeSection.Span; // 2 - read immediate operand initcontainer_index, encoded as 8-bit unsigned value @@ -350,13 +430,11 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt // 3 - pop value, salt, input_offset, input_size from the operand stack // no stack checks becaue EOF guarantees no stack undeflows - stack.PopUInt256(out UInt256 value); - stack.PopWord256(out Span salt); - stack.PopUInt256(out UInt256 dataOffset); - stack.PopUInt256(out UInt256 dataSize); + if (!stack.PopUInt256(out UInt256 value) || stack.PopWord256(out Span salt) || stack.PopUInt256(out UInt256 dataOffset) || stack.PopUInt256(out UInt256 dataSize)) + goto OutOfGas; // 4 - perform (and charge for) memory expansion using [input_offset, input_size] - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataSize)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataSize)) goto OutOfGas; // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header @@ -366,14 +444,14 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt { //if (!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) // return (EvmExceptionType.OutOfGas, null); - if (initContainer.Length > spec.MaxInitCodeSize) return EvmExceptionType.OutOfGas; + if (initContainer.Length > spec.MaxInitCodeSize) goto OutOfGas; } // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) long numberOfWordsInInitCode = EvmPooledMemory.Div32Ceiling((UInt256)initContainer.Length); long hashCost = GasCostOf.Sha3Word * numberOfWordsInInitCode; if (!UpdateGas(hashCost, ref gasAvailable)) - return EvmExceptionType.OutOfGas; + goto OutOfGas; IWorldState state = vm.WorldState; // 7 - check that current call depth is below STACK_DEPTH_LIMIT and that caller balance is enough to transfer value @@ -392,7 +470,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt // 9 - execute the container and deduct gas for execution. The 63/64th rule from EIP-150 applies. long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; - if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(callGas, ref gasAvailable)) goto OutOfGas; // 10 - increment sender account’s nonce UInt256 accountNonce = state.GetNonce(env.ExecutingAccount); @@ -465,16 +543,22 @@ in vm.EvmState.AccessTracker ); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StaticCallViolation: + return EvmExceptionType.StaticCallViolation; + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { if (!vm.EvmState.ExecutionType.IsAnyCreateEof()) - return EvmExceptionType.BadInstruction; - + goto BadInstruction; - if (!UpdateGas(GasCostOf.ReturnContract, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.ReturnContract, ref gasAvailable)) goto OutOfGas; IReleaseSpec spec = vm.Spec; ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; @@ -486,7 +570,7 @@ public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref stack.PopUInt256(out UInt256 a); stack.PopUInt256(out UInt256 b); - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, b)) goto OutOfGas; int projectedNewSize = (int)b + deployCodeInfo.DataSection.Length; if (projectedNewSize < deployCodeInfo.EofContainer.Header.DataSection.Size || projectedNewSize > UInt16.MaxValue) @@ -498,6 +582,11 @@ public static EvmExceptionType InstructionReturnContract(VirtualMachine vm, ref vm.ReturnData = deployCodeInfo; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + BadInstruction: + return EvmExceptionType.BadInstruction; } [SkipLocalsInit] @@ -506,16 +595,21 @@ public static EvmExceptionType InstructionReturnDataLoad(VirtualMachine vm, ref IReleaseSpec spec = vm.Spec; ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (!spec.IsEofEnabled || codeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; ZeroPaddedSpan slice = vm.ReturnDataBuffer.Span.SliceWithZeroPadding(a, 32); stack.PushBytes(slice); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; + BadInstruction: + return EvmExceptionType.BadInstruction; } public interface IOpEofCall @@ -555,12 +649,11 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, // Instruction is undefined in legacy code and only available in EOF if (env.CodeInfo.Version == 0) - return EvmExceptionType.BadInstruction; + goto BadInstruction; // 1. Pop required arguments from stack, halt with exceptional failure on stack underflow. - stack.PopWord256(out Span targetBytes); - stack.PopUInt256(out UInt256 dataOffset); - stack.PopUInt256(out UInt256 dataLength); + if (!stack.PopWord256(out Span targetBytes) || stack.PopUInt256(out UInt256 dataOffset) || stack.PopUInt256(out UInt256 dataLength)) + goto StackUnderflow; UInt256 transferValue; UInt256 callValue; @@ -580,21 +673,21 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, } else { - return EvmExceptionType.StackUnderflow; + goto StackUnderflow; } // 3. If value is non-zero: // a: Halt with exceptional failure if the current frame is in static-mode. - if (vm.EvmState.IsStatic && !transferValue.IsZero) return EvmExceptionType.StaticCallViolation; + if (vm.EvmState.IsStatic && !transferValue.IsZero) goto StaticCallViolation; // b. Charge CALL_VALUE_COST gas. - if (typeof(TOpEofCall) == typeof(OpEofCall) && !transferValue.IsZero && !UpdateGas(GasCostOf.CallValue, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (typeof(TOpEofCall) == typeof(OpEofCall) && !transferValue.IsZero && !UpdateGas(GasCostOf.CallValue, ref gasAvailable)) goto OutOfGas; // 4. If target_address has any of the high 12 bytes set to a non-zero value // (i.e. it does not contain a 20-byte address) if (!targetBytes[0..12].IsZero()) { // then halt with an exceptional failure. - return EvmExceptionType.AddressOutOfRange; + goto AddressOutOfRange; } Address caller = typeof(TOpEofCall) == typeof(OpEofDelegateCall) ? env.Caller : env.ExecutingAccount; @@ -604,17 +697,17 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, : codeSource; // 5. Perform (and charge for) memory expansion using [input_offset, input_size]. - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, in dataLength)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, in dataLength)) goto OutOfGas; // 1. Charge WARM_STORAGE_READ_COST (100) gas. // 6. If target_address is not in the warm_account_list, charge COLD_ACCOUNT_ACCESS - WARM_STORAGE_READ_COST (2500) gas. - if (!ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) goto OutOfGas; if ((!spec.ClearEmptyAccountWhenTouched && !state.AccountExists(codeSource)) || (spec.ClearEmptyAccountWhenTouched && transferValue != 0 && state.IsDeadAccount(codeSource))) { // 7. If target_address is not in the state and the call configuration would result in account creation, // charge ACCOUNT_CREATION_COST (25000) gas. (The only such case in this EIP is if value is non-zero.) - if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) goto OutOfGas; } // 8. Calculate the gas available to callee as caller’s remaining gas reduced by max(floor(gas/64), MIN_RETAINED_GAS). @@ -673,7 +766,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, } // 10. Perform the call with the available gas and configuration. - if (!UpdateGas(callGas, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(callGas, ref gasAvailable)) goto OutOfGas; ReadOnlyMemory callData = vm.EvmState.Memory.Load(in dataOffset, dataLength); @@ -706,5 +799,16 @@ in vm.EvmState.AccessTracker ); 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.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs index 03c90f26204..4ea94375767 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs @@ -19,22 +19,24 @@ public static EvmExceptionType InstructionGas(VirtualMachine _, ref EvmStack sta gasAvailable -= GasCostOf.Base; // Ensure gas is positive before pushing to stack - if (gasAvailable < 0) return EvmExceptionType.OutOfGas; + if (gasAvailable < 0) goto OutOfGas; stack.PushUInt256((UInt256)gasAvailable); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; } [SkipLocalsInit] public static EvmExceptionType InstructionBlobHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; - if (!spec.IsEip4844Enabled) return EvmExceptionType.BadInstruction; gasAvailable -= GasCostOf.BlobHash; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; byte[][] versionedHashes = vm.EvmState.Env.TxExecutionContext.BlobVersionedHashes; @@ -48,16 +50,17 @@ public static EvmExceptionType InstructionBlobHash(VirtualMachine vm, ref EvmSta } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - Metrics.BlockhashOpcode++; - gasAvailable -= GasCostOf.BlockHash; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; long number = a > long.MaxValue ? long.MaxValue : (long)a; Hash256? blockHash = vm.BlockHashProvider.GetBlockhash(vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header, number); @@ -70,5 +73,8 @@ public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmSt } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs index 11486a7d6e0..79720089aa6 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs @@ -31,10 +31,15 @@ public static EvmExceptionType InstructionJumpDest(VirtualMachine _, ref EvmStac public static EvmExceptionType InstructionJump(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Mid; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; - if (!Jump(result, ref programCounter, in vm.EvmState.Env)) return EvmExceptionType.InvalidJumpDestination; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + if (!Jump(result, ref programCounter, in vm.EvmState.Env)) goto InvalidJumpDestination; return EvmExceptionType.None; + // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; + InvalidJumpDestination: + return EvmExceptionType.InvalidJumpDestination; } [SkipLocalsInit] @@ -42,15 +47,20 @@ public static EvmExceptionType InstructionJump(VirtualMachine vm, ref EvmStack s public static EvmExceptionType InstructionJumpIf(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.High; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; ref byte condition = ref stack.PopBytesByRef(); - if (Unsafe.IsNullRef(in condition)) return EvmExceptionType.StackUnderflow; + if (Unsafe.IsNullRef(in condition)) goto StackUnderflow; if (Unsafe.As>(ref condition) != default) { - if (!Jump(result, ref programCounter, in vm.EvmState.Env)) return EvmExceptionType.InvalidJumpDestination; + if (!Jump(result, ref programCounter, in vm.EvmState.Env)) goto InvalidJumpDestination; } return EvmExceptionType.None; + // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; + InvalidJumpDestination: + return EvmExceptionType.InvalidJumpDestination; } [SkipLocalsInit] diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs index de5d49e3ff1..d1781462f64 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs @@ -22,13 +22,16 @@ public static EvmExceptionType InstructionMath1Param(VirtualMachine _, gasAvailable -= TOpMath.GasCost; ref byte bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) return EvmExceptionType.StackUnderflow; + if (IsNullRef(ref bytesRef)) goto StackUnderflow; Vector256 result = TOpMath.Operation(ref bytesRef); WriteUnaligned(ref stack.PushBytesRef(), result); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } public struct OpNot : IOpMath1Param diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs index 8ac520c8088..eaefb5ce0ba 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs @@ -22,14 +22,16 @@ public static EvmExceptionType InstructionMath2Param(VirtualMachine _, { gasAvailable -= TOpMath.GasCost; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) goto StackUnderflow; TOpMath.Operation(in a, in b, out UInt256 result); stack.PushUInt256(in result); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } public struct OpAdd : IOpMath2Param diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs index e5ac578de73..b565004c922 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs @@ -20,9 +20,7 @@ public static EvmExceptionType InstructionMath3Param(VirtualMachine _, { gasAvailable -= TOpMath.GasCost; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 c)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) goto StackUnderflow; if (c.IsZero) { @@ -35,6 +33,9 @@ public static EvmExceptionType InstructionMath3Param(VirtualMachine _, } return EvmExceptionType.None; + StackUnderflow: + // Jump forward to be unpredicted by the branch predictor + return EvmExceptionType.StackUnderflow; } public struct OpAddMod : IOpMath3Param diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs index 4fa5216bdb6..30de7b87542 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs @@ -21,20 +21,23 @@ public static EvmExceptionType InstructionShift(VirtualMachine vm, ref { gasAvailable -= TOpShift.GasCost; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; if (a >= 256) { - if (!stack.PopLimbo()) return EvmExceptionType.StackUnderflow; + if (!stack.PopLimbo()) goto StackUnderflow; stack.PushZero(); } else { - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 b)) goto StackUnderflow; TOpShift.Operation(in a, in b, out UInt256 result); stack.PushUInt256(in result); } return EvmExceptionType.None; + // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -42,8 +45,7 @@ public static EvmExceptionType InstructionSar(VirtualMachine vm, ref EvmStack st { gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) goto StackUnderflow; if (a >= 256) { @@ -63,6 +65,9 @@ public static EvmExceptionType InstructionSar(VirtualMachine vm, ref EvmStack st } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } public struct OpShl : IOpShift diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index ba67bf8eab8..e16b6f75758 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -55,9 +55,12 @@ public static EvmExceptionType InstructionDup(VirtualMachine _, ref Ev where TOpCount : IOpCount { gasAvailable -= GasCostOf.VeryLow; - if (!stack.Dup(TOpCount.Count)) return EvmExceptionType.StackUnderflow; + if (!stack.Dup(TOpCount.Count)) goto StackUnderflow; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -65,9 +68,12 @@ public static EvmExceptionType InstructionSwap(VirtualMachine _, ref E where TOpCount : IOpCount { gasAvailable -= GasCostOf.VeryLow; - if (!stack.Swap(TOpCount.Count + 1)) return EvmExceptionType.StackUnderflow; + if (!stack.Swap(TOpCount.Count + 1)) goto StackUnderflow; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -102,15 +108,14 @@ public static EvmExceptionType InstructionLog(VirtualMachine vm, ref E where TOpCount : struct, IOpCount { EvmState vmState = vm.EvmState; - if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; + if (vmState.IsStatic) goto StaticCallViolation; - if (!stack.PopUInt256(out UInt256 position)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 position) || !stack.PopUInt256(out UInt256 length)) goto StackUnderflow; long topicsCount = TOpCount.Count; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, length)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, length)) goto OutOfGas; if (!UpdateGas( GasCostOf.Log + topicsCount * GasCostOf.LogTopic + - (long)length * GasCostOf.LogData, ref gasAvailable)) return EvmExceptionType.OutOfGas; + (long)length * GasCostOf.LogData, ref gasAvailable)) goto OutOfGas; ReadOnlyMemory data = vmState.Memory.Load(in position, length); Hash256[] topics = new Hash256[topicsCount]; @@ -131,5 +136,12 @@ public static EvmExceptionType InstructionLog(VirtualMachine vm, ref E } return EvmExceptionType.None; + // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + StackUnderflow: + return EvmExceptionType.StackUnderflow; + StaticCallViolation: + return EvmExceptionType.StaticCallViolation; + OutOfGas: + return EvmExceptionType.OutOfGas; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index b91de51b91a..045e164b569 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -27,7 +27,7 @@ public static EvmExceptionType InstructionTLoad(VirtualMachine vm, ref EvmStack Metrics.TloadOpcode++; gasAvailable -= GasCostOf.TLoad; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; StorageCell storageCell = new(vm.EvmState.Env.ExecutingAccount, result); ReadOnlySpan value = vm.WorldState.GetTransientState(in storageCell); @@ -35,11 +35,16 @@ public static EvmExceptionType InstructionTLoad(VirtualMachine vm, ref EvmStack if (vm.TxTracer.IsTracingStorage) { - if (gasAvailable < 0) return EvmExceptionType.OutOfGas; + if (gasAvailable < 0) goto OutOfGas; vm.TxTracer.LoadOperationTransientStorage(storageCell.Address, result, value); } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] @@ -48,22 +53,29 @@ public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack Metrics.TstoreOpcode++; EvmState vmState = vm.EvmState; - if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; + if (vmState.IsStatic) goto StaticCallViolation; gasAvailable -= GasCostOf.TStore; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; StorageCell storageCell = new(vmState.Env.ExecutingAccount, result); Span bytes = stack.PopWord256(); vm.WorldState.SetTransientState(in storageCell, !bytes.IsZero() ? bytes.ToArray() : BytesZero32); if (vm.TxTracer.IsTracingStorage) { - if (gasAvailable < 0) return EvmExceptionType.OutOfGas; + if (gasAvailable < 0) goto OutOfGas; ReadOnlySpan currentValue = vm.WorldState.GetTransientState(in storageCell); vm.TxTracer.SetOperationTransientStorage(storageCell.Address, result, bytes, currentValue); } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + StaticCallViolation: + return EvmExceptionType.StaticCallViolation; } [SkipLocalsInit] @@ -71,13 +83,11 @@ public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack { Metrics.MCopyOpcode++; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 c)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) goto StackUnderflow; gasAvailable -= GasCostOf.VeryLow + GasCostOf.VeryLow * EvmPooledMemory.Div32Ceiling(c); EvmState vmState = vm.EvmState; - if (!UpdateMemoryCost(vmState, ref gasAvailable, UInt256.Max(b, a), c)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(vmState, ref gasAvailable, UInt256.Max(b, a), c)) goto OutOfGas; Span bytes = vmState.Memory.LoadSpan(in b, c); @@ -88,6 +98,11 @@ public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack tracer?.ReportMemoryChange(a, bytes); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } @@ -97,19 +112,19 @@ internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmSta { Metrics.IncrementSStoreOpcode(); EvmState vmState = vm.EvmState; - if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; + if (vmState.IsStatic) goto StaticCallViolation; IReleaseSpec spec = vm.Spec; // fail fast before the first storage read if gas is not enough even for reset - if (!spec.UseNetGasMetering && !UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!spec.UseNetGasMetering && !UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) goto OutOfGas; if (spec.UseNetGasMeteringWithAStipendFix) { if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportExtraGasPressure(GasCostOf.CallStipend - spec.GetNetMeteredSStoreCost() + 1); - if (gasAvailable <= GasCostOf.CallStipend) return EvmExceptionType.OutOfGas; + if (gasAvailable <= GasCostOf.CallStipend) goto OutOfGas; } - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; ReadOnlySpan bytes = stack.PopWord256(); bool newIsZero = bytes.IsZero(); bytes = !newIsZero ? bytes.WithoutLeadingZeros() : BytesZero; @@ -121,7 +136,7 @@ internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmSta vm, in storageCell, StorageAccessType.SSTORE, - spec)) return EvmExceptionType.OutOfGas; + spec)) goto OutOfGas; ReadOnlySpan currentValue = vm.WorldState.Get(in storageCell); // Console.WriteLine($"current: {currentValue.ToHexString()} newValue {newValue.ToHexString()}"); @@ -142,14 +157,14 @@ internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmSta } else if (currentIsZero) { - if (!UpdateGas(GasCostOf.SSet - GasCostOf.SReset, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.SSet - GasCostOf.SReset, ref gasAvailable)) goto OutOfGas; } } else // net metered { if (newSameAsCurrent) { - if (!UpdateGas(spec.GetNetMeteredSStoreCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(spec.GetNetMeteredSStoreCost(), ref gasAvailable)) goto OutOfGas; } else // net metered, C != N { @@ -161,11 +176,11 @@ internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmSta { if (currentIsZero) { - if (!UpdateGas(GasCostOf.SSet, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.SSet, ref gasAvailable)) goto OutOfGas; } else // net metered, current == original != new, !currentIsZero { - if (!UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) goto OutOfGas; if (newIsZero) { @@ -177,7 +192,7 @@ internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmSta else // net metered, new != current != original { long netMeteredStoreCost = spec.GetNetMeteredSStoreCost(); - if (!UpdateGas(netMeteredStoreCost, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(netMeteredStoreCost, ref gasAvailable)) goto OutOfGas; if (!originalIsZero) // net metered, new != current != original != 0 { @@ -233,6 +248,13 @@ internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmSta } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + StaticCallViolation: + return EvmExceptionType.StaticCallViolation; } [SkipLocalsInit] @@ -243,14 +265,14 @@ internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStac Metrics.IncrementSLoadOpcode(); gasAvailable -= spec.GetSLoadCost(); - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; StorageCell storageCell = new(vm.EvmState.Env.ExecutingAccount, result); if (!ChargeStorageAccessGas( ref gasAvailable, vm, in storageCell, StorageAccessType.SLOAD, - spec)) return EvmExceptionType.OutOfGas; + spec)) goto OutOfGas; ReadOnlySpan value = vm.WorldState.Get(in storageCell); stack.PushBytes(value); @@ -260,6 +282,11 @@ internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStac } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } internal static bool ChargeStorageAccessGas( diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index ab36a742d51..7d05370cec4 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -98,10 +98,18 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) { lookup[(int)Instruction.SELFBALANCE] = &InstructionSelfBalance; } - - lookup[(int)Instruction.BASEFEE] = &InstructionEnvUInt256; - lookup[(int)Instruction.BLOBHASH] = &InstructionBlobHash; - lookup[(int)Instruction.BLOBBASEFEE] = &InstructionEnvUInt256; + if (spec.BaseFeeEnabled) + { + lookup[(int)Instruction.BASEFEE] = &InstructionEnvUInt256; + } + if (spec.IsEip4844Enabled) + { + lookup[(int)Instruction.BLOBHASH] = &InstructionBlobHash; + } + if (spec.BlobBaseFeeEnabled) + { + lookup[(int)Instruction.BLOBBASEFEE] = &InstructionEnvUInt256; + } // Gap: 0x4b to 0x4f lookup[(int)Instruction.POP] = &InstructionPop; lookup[(int)Instruction.MLOAD] = &InstructionMLoad; diff --git a/src/Nethermind/Nethermind.Evm/Metrics.cs b/src/Nethermind/Nethermind.Evm/Metrics.cs index beec7fadeca..edcaa4f644f 100644 --- a/src/Nethermind/Nethermind.Evm/Metrics.cs +++ b/src/Nethermind/Nethermind.Evm/Metrics.cs @@ -60,9 +60,6 @@ public class Metrics [Description("Number of EXP opcodes executed.")] public static long ExpOpcode { get; set; } - [Description("Number of BLOCKHASH opcodes executed.")] - public static long BlockhashOpcode { get; set; } - [Description("Number of BN254_MUL precompile calls.")] public static long Bn254MulPrecompile { get; set; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d6f3c879851..e26b03a346c 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; @@ -90,6 +91,7 @@ public sealed unsafe class VirtualMachine : IVirtualMachine private int _sectionIndex; private OpCode[] _opcodeMethods; + private static long _txExecutedTracker; public VirtualMachine( IBlockhashProvider? blockHashProvider, @@ -104,25 +106,18 @@ public VirtualMachine( _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); } - public VirtualMachine( - IBlockhashProvider? blockhashProvider, - ISpecProvider? specProvider, - ICodeInfoRepository codeInfoRepository, - ILogger? logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _blockHashProvider = blockhashProvider ?? throw new ArgumentNullException(nameof(blockhashProvider)); - _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); - _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); - } - public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) { _txTracer = txTracer; _state = worldState; _spec = _specProvider.GetSpec(state.Env.TxExecutionContext.BlockExecutionContext.Header.Number, state.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp); + if (_txExecutedTracker < 250_000 && Interlocked.Increment(ref _txExecutedTracker) % 10_000 == 0) + { + if (_logger.IsDebug) _logger.Debug("Resetting EVM instructions cache"); + // Flush the cache every 10_000 transactions to directly point at any PGO optimized methods rather than via pre-stubs + _spec.EvmInstructions = EvmInstructions.GenerateOpCodes(_spec); + } _opcodeMethods = (OpCode[])(_spec.EvmInstructions ??= EvmInstructions.GenerateOpCodes(_spec)); ref readonly TxExecutionContext txExecutionContext = ref state.Env.TxExecutionContext; ICodeInfoRepository codeInfoRepository = txExecutionContext.CodeInfoRepository; From 84c16ed0ad0aacee811ff998e7c49051062fd341 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 19:59:37 +0000 Subject: [PATCH 194/255] Data copy fix --- .../Nethermind.Evm/Instructions/EvmInstructions.Eof.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 0bdb60b6f1c..4f27d557418 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -152,8 +152,6 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta } } - stack.PushUInt32(codeInfo.DataSection.Length); - return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor StackUnderflow: From f77e9969cd7b2d1a8b4b23c5852e72ff9383d893 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 20:08:04 +0000 Subject: [PATCH 195/255] Fix stack underflow check --- .../Nethermind.Evm/Instructions/EvmInstructions.Eof.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 4f27d557418..b85d9f1b4e9 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -135,7 +135,7 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta if (codeInfo.Version == 0) goto BadInstruction; - if (!stack.PopUInt256(out UInt256 memOffset) || stack.PopUInt256(out UInt256 offset) || stack.PopUInt256(out UInt256 size)) goto StackUnderflow; + if (!stack.PopUInt256(out UInt256 memOffset) || !stack.PopUInt256(out UInt256 offset) || !stack.PopUInt256(out UInt256 size)) goto StackUnderflow; if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable)) goto OutOfGas; From a0cf43ae86738045dd8fa96be3a8ea2c081a6fe0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 20:18:19 +0000 Subject: [PATCH 196/255] Fix EofCall --- .../Nethermind.Evm/Instructions/EvmInstructions.Eof.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index b85d9f1b4e9..18ca421b850 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -650,7 +650,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, goto BadInstruction; // 1. Pop required arguments from stack, halt with exceptional failure on stack underflow. - if (!stack.PopWord256(out Span targetBytes) || stack.PopUInt256(out UInt256 dataOffset) || stack.PopUInt256(out UInt256 dataLength)) + if (!stack.PopWord256(out Span targetBytes) || !stack.PopUInt256(out UInt256 dataOffset) || !stack.PopUInt256(out UInt256 dataLength)) goto StackUnderflow; UInt256 transferValue; From 00410ee8cd3beb8a20351fa664a37aff1e939be1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 1 Feb 2025 21:39:24 +0000 Subject: [PATCH 197/255] Fix EofCreate --- .../Nethermind.Evm/Instructions/EvmInstructions.Eof.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 18ca421b850..f62cce34070 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -428,7 +428,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt // 3 - pop value, salt, input_offset, input_size from the operand stack // no stack checks becaue EOF guarantees no stack undeflows - if (!stack.PopUInt256(out UInt256 value) || stack.PopWord256(out Span salt) || stack.PopUInt256(out UInt256 dataOffset) || stack.PopUInt256(out UInt256 dataSize)) + if (!stack.PopUInt256(out UInt256 value) || !stack.PopWord256(out Span salt) || !stack.PopUInt256(out UInt256 dataOffset) || !stack.PopUInt256(out UInt256 dataSize)) goto OutOfGas; // 4 - perform (and charge for) memory expansion using [input_offset, input_size] From f5853befa41a24a2a348dc84b75db66ef2a10aa6 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 2 Feb 2025 16:37:25 +0000 Subject: [PATCH 198/255] Fix gas for failed EOF validation --- .../OsakaStateTests.cs | 1 - src/Nethermind/Nethermind.Evm/EvmState.cs | 2 +- .../TransactionProcessor.cs | 17 +++++++++++------ src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 11 +++++++++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs index 7537f077c2d..315a9a3454d 100644 --- a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/OsakaStateTests.cs @@ -11,7 +11,6 @@ namespace Ethereum.Blockchain.Pyspec.Test; [TestFixture] [Parallelizable(ParallelScope.All)] -[Explicit("These tests are not yet updated to devnet-6")] public class OsakaStateTests : GeneralStateTestBase { [TestCaseSource(nameof(LoadTests))] diff --git a/src/Nethermind/Nethermind.Evm/EvmState.cs b/src/Nethermind/Nethermind.Evm/EvmState.cs index eb56c021e12..769549cb207 100644 --- a/src/Nethermind/Nethermind.Evm/EvmState.cs +++ b/src/Nethermind/Nethermind.Evm/EvmState.cs @@ -160,7 +160,7 @@ ExecutionType.STATICCALL or ExecutionType.CALL or ExecutionType.CALLCODE or Exec }; public Address To => Env.CodeSource ?? Env.ExecutingAccount; - internal bool IsPrecompile => Env.CodeInfo.IsPrecompile; + internal bool IsPrecompile => Env.CodeInfo?.IsPrecompile ?? false; public ref readonly StackAccessTracker AccessTracker => ref _accessTracker; public ref readonly ExecutionEnvironment Env => ref _env; diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 2f0897bf386..40b73a35efb 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -167,7 +167,6 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon long gasAvailable = tx.GasLimit - intrinsicGas.Standard; if (!(result = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, _codeInfoRepository, accessTracker, out ExecutionEnvironment env))) return result; - ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas.FloorGas, accessTracker, gasAvailable, env, out TransactionSubstate? substate, out GasConsumed spentGas, out byte statusCode); PayFees(tx, header, spec, tracer, substate, spentGas.SpentGas, premiumPerGas, blobBaseFee, statusCode); @@ -564,8 +563,8 @@ private TransactionResult BuildExecutionEnvironment( if (delegationAddress is not null) accessTracker.WarmUp(delegationAddress); } - codeInfo ??= CodeInfo.Empty; - codeInfo.AnalyseInBackgroundIfRequired(); + codeInfo?.AnalyseInBackgroundIfRequired(); + if (spec.UseHotAndColdStorage) { if (spec.UseTxAccessLists) @@ -635,6 +634,13 @@ protected virtual void 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. + gasConsumed = floorGas; + goto Fail; + } ExecutionType executionType = tx.IsContractCreation ? (tx.IsEofContractCreation ? ExecutionType.TXCREATE : ExecutionType.CREATE) : ExecutionType.TRANSACTION; @@ -772,8 +778,7 @@ protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec if (statusCode == StatusCode.Failure || gasBeneficiaryNotDestroyed) { UInt256 fees = (UInt256)spentGas * premiumPerGas; - UInt256 eip1559Fees = !tx.IsFree() ? (UInt256)spentGas * header.BaseFeePerGas : UInt256.Zero; - UInt256 collectedFees = spec.IsEip1559Enabled ? eip1559Fees : UInt256.Zero; + UInt256 collectedFees = spec.IsEip1559Enabled && !tx.IsFree() ? (UInt256)spentGas * header.BaseFeePerGas : UInt256.Zero; if (tx.SupportsBlobs && spec.IsEip4844FeeCollectorEnabled) { @@ -786,7 +791,7 @@ protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec WorldState.AddToBalanceAndCreateIfNotExists(spec.FeeCollector, collectedFees, spec); if (tracer.IsTracingFees) - tracer.ReportFees(fees, eip1559Fees + blobBaseFee); + tracer.ReportFees(fees, collectedFees); } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index e26b03a346c..20a0a98a7ff 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -179,10 +179,17 @@ public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer : currentState.Env.InputData, currentState.ExecutionType); - if (_txTracer.IsTracingCode) _txTracer.ReportByteCode(currentState.Env.CodeInfo.MachineCode); + if (_txTracer.IsTracingCode) _txTracer.ReportByteCode(currentState.Env.CodeInfo?.MachineCode ?? default); } - callResult = ExecuteCall(currentState, previousCallResult, previousCallOutput, previousCallOutputDestination); + if (currentState.Env.CodeInfo is not null) + { + callResult = ExecuteCall(currentState, previousCallResult, previousCallOutput, previousCallOutputDestination); + } + else + { + callResult = CallResult.InvalidCodeException; + } if (!callResult.IsReturn) { From 740d5153f80d4b04563468e679cdf9285d4299fa Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 2 Feb 2025 18:53:37 +0000 Subject: [PATCH 199/255] Trailing bytes --- .../Ethereum.Test.Base/GeneralTestBase.cs | 24 +++++++++---------- .../EvmObjectFormat/Handlers/EofV1.cs | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs index 01002c5d9fc..390afb5b46b 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs @@ -4,36 +4,34 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; +using System.Threading.Tasks; +using Autofac; +using NUnit.Framework; +using Nethermind.Config; using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; +using Nethermind.Core.Test; +using Nethermind.Core.Test.Modules; using Nethermind.Crypto; -using Nethermind.Db; -using Nethermind.Int256; using Nethermind.Evm; +using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.Tracing; using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; using Nethermind.State; -using Nethermind.Trie.Pruning; -using NUnit.Framework; -using System.Threading.Tasks; -using Autofac; -using Nethermind.Config; -using Nethermind.Core.Test.Builders; -using Nethermind.Core.Test.Modules; namespace Ethereum.Test.Base { public abstract class GeneralStateTestBase { - private static ILogger _logger = new(new ConsoleAsyncLogger(LogLevel.Info)); + private static ILogger _logger = new(new NUnitLogger(LogLevel.Trace)); private static ILogManager _logManager = new TestLogManager(LogLevel.Warn); private static readonly UInt256 _defaultBaseFeeForStateTest = 0xA; private readonly TxValidator _txValidator = new(MainnetSpecProvider.Instance.ChainId); @@ -49,7 +47,6 @@ public void Setup() protected static void Setup(ILogManager logManager) { _logManager = logManager ?? LimboLogs.Instance; - _logger = _logManager.GetClassLogger(); } protected EthereumTestResult RunTest(GeneralStateTest test) @@ -59,9 +56,10 @@ protected EthereumTestResult RunTest(GeneralStateTest test) protected EthereumTestResult RunTest(GeneralStateTest test, ITxTracer txTracer) { - TestContext.Out.Write($"Running {test.Name} at {DateTime.UtcNow:HH:mm:ss.ffffff}"); + TestContext.Out.WriteLine($"Running {test.Name} at {DateTime.UtcNow:HH:mm:ss.ffffff}"); Assert.That(test.LoadFailure, Is.Null, "test data loading failure"); + EofValidator.Logger = _logger; ISpecProvider specProvider = test.ChainId == GnosisSpecProvider.Instance.ChainId ? GnosisSpecProvider.Instance : new CustomSpecProvider( diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index a1143910209..39d6b952775 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -346,7 +346,7 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => : new CompoundSectionHeader(codeSectionSubHeader.EndOffset, containerSections); var dataSectionSubHeader = new SectionHeader(containerSectionSubHeader?.EndOffset ?? codeSectionSubHeader.EndOffset, sectionSizes.DataSectionSize.Value); - if (dataSectionSubHeader.EndOffset < containerMemory.Length) + if (!validationStrategy.HasFlag(ValidationStrategy.AllowTrailingBytes) && dataSectionSubHeader.EndOffset < containerMemory.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Extra data after end of container, starting at {dataSectionSubHeader.EndOffset}"); return false; From f7617ba3e4497c7eae643474de9e951b1a3f6cbf Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 2 Feb 2025 20:03:49 +0000 Subject: [PATCH 200/255] Optimize --- .../Instructions/EvmInstructions.Create.cs | 7 ++-- .../Instructions/EvmInstructions.Eof.cs | 6 +-- .../Instructions/EvmInstructions.Storage.cs | 3 +- .../Instructions/EvmInstructions.cs | 11 +++-- .../Nethermind.Evm/VirtualMachine.cs | 40 ++++++++++++++----- 5 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index dcf834927d3..cd8560df6e3 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -57,13 +57,14 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r if (initCodeLength > spec.MaxInitCodeSize) goto OutOfGas; } + bool outOfGas = false; long gasCost = GasCostOf.Create + - (spec.IsEip3860Enabled ? GasCostOf.InitCodeWord * EvmPooledMemory.Div32Ceiling(in initCodeLength) : 0) + + (spec.IsEip3860Enabled ? GasCostOf.InitCodeWord * EvmPooledMemory.Div32Ceiling(in initCodeLength, out outOfGas) : 0) + (typeof(TOpCreate) == typeof(OpCreate2) - ? GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in initCodeLength) + ? GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in initCodeLength, out outOfGas) : 0); - if (!UpdateGas(gasCost, ref gasAvailable)) goto OutOfGas; + if (outOfGas || !UpdateGas(gasCost, ref gasAvailable)) goto OutOfGas; if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memoryPositionOfInitCode, in initCodeLength)) goto OutOfGas; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index f62cce34070..e02aa9db836 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -137,7 +137,7 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta if (!stack.PopUInt256(out UInt256 memOffset) || !stack.PopUInt256(out UInt256 offset) || !stack.PopUInt256(out UInt256 size)) goto StackUnderflow; - if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size), ref gasAvailable)) + if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size, out bool outOfGas), ref gasAvailable) || outOfGas) goto OutOfGas; if (!size.IsZero) @@ -446,9 +446,9 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt } // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) - long numberOfWordsInInitCode = EvmPooledMemory.Div32Ceiling((UInt256)initContainer.Length); + long numberOfWordsInInitCode = EvmPooledMemory.Div32Ceiling((UInt256)initContainer.Length, out bool outOfGas); long hashCost = GasCostOf.Sha3Word * numberOfWordsInInitCode; - if (!UpdateGas(hashCost, ref gasAvailable)) + if (outOfGas || !UpdateGas(hashCost, ref gasAvailable)) goto OutOfGas; IWorldState state = vm.WorldState; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index 045e164b569..f7d030619b6 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -85,7 +85,8 @@ public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) goto StackUnderflow; - gasAvailable -= GasCostOf.VeryLow + GasCostOf.VeryLow * EvmPooledMemory.Div32Ceiling(c); + gasAvailable -= GasCostOf.VeryLow + GasCostOf.VeryLow * EvmPooledMemory.Div32Ceiling(c, out bool outOfGas); + if (outOfGas) goto OutOfGas; EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, UInt256.Max(b, a), c)) goto OutOfGas; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 7d05370cec4..1dd7ff6954a 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -480,9 +480,9 @@ public static EvmExceptionType InstructionSignExtend(VirtualMachine _, ref EvmSt [SkipLocalsInit] public static EvmExceptionType InstructionKeccak256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; - if (!stack.PopUInt256(out UInt256 b)) return EvmExceptionType.StackUnderflow; - gasAvailable -= GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in b); + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) goto StackUnderflow; + gasAvailable -= GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in b, out bool outOfGas); + if (outOfGas) goto OutOfGas; EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; @@ -491,6 +491,11 @@ public static EvmExceptionType InstructionKeccak256(VirtualMachine vm, ref EvmSt stack.PushBytes(ValueKeccak.Compute(bytes).BytesAsSpan); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } [SkipLocalsInit] diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 20a0a98a7ff..a440a35ae83 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -631,11 +631,17 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousC _vmState = vmState; // Struct generic parameter is used to burn out all the if statements - // and inner code by typeof(TTracing) == typeof(NotTracing) - // checks that are evaluated to constant values at compile time. + // and inner code by using static property on generic IFlag using + // OnFlag or OffFlag. These checks that are evaluated to constant values at compile time. // This only works for structs, not for classes or interface types // which use shared generics. - return ExecuteCode(ref stack, gasAvailable); + return (tracing: _txTracer.IsTracingInstructions, cancelable: _txTracer.IsCancelable) switch + { + (tracing: false, cancelable: false) => ExecuteCode(ref stack, gasAvailable), + (tracing: false, cancelable: true) => ExecuteCode(ref stack, gasAvailable), + (tracing: true, cancelable: false) => ExecuteCode(ref stack, gasAvailable), + (tracing: true, cancelable: true) => ExecuteCode(ref stack, gasAvailable) + }; Empty: return CallResult.Empty(vmState.Env.CodeInfo.Version); OutOfGas: @@ -643,7 +649,9 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousC } [SkipLocalsInit] - private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailable) + private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailable) + where TTracingInstructions : struct, IFlag + where TCancelable : struct, IFlag { _returnData = null; _sectionIndex = _vmState.FunctionIndex; @@ -656,9 +664,6 @@ private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailab DebugTracer? debugger = _txTracer.GetTracer(); #endif - var tracingInstructions = _txTracer.IsTracingInstructions; - var isCancelable = _txTracer.IsCancelable; - OpCode[] opcodeMethods = _opcodeMethods; // Initialize program counter to the current state's value. // Entry point is not always 0 as we may be returning to code after a call. @@ -673,9 +678,8 @@ private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailab // Get the opcode at the current program counter Instruction instruction = codeSection[programCounter]; - if (isCancelable && _txTracer.IsCancelled) ThrowOperationCanceledException(); - - if (tracingInstructions) + if (TCancelable.IsActive && _txTracer.IsCancelled) ThrowOperationCanceledException(); + if (TTracingInstructions.IsActive) StartInstructionTrace(instruction, gasAvailable, programCounter, in stack); // Advance the program counter one instruction @@ -694,7 +698,7 @@ private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailab // Exit loop if returning data if (_returnData is not null) break; - if (tracingInstructions) + if (TTracingInstructions.IsActive) EndInstructionTrace(gasAvailable); } @@ -816,6 +820,20 @@ private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExc _txTracer.ReportOperationError(evmExceptionType); } + interface IFlag + { + virtual static bool IsActive { get; } + } + + struct OffFlag : IFlag + { + public static bool IsActive => false; + } + struct OnFlag : IFlag + { + public static bool IsActive => true; + } + internal readonly ref struct CallResult { public static CallResult InvalidSubroutineEntry => new(EvmExceptionType.InvalidSubroutineEntry); From fac8a61d33d2ebb6ac7cb3d8f2594422ed9a75ef Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 2 Feb 2025 21:18:47 +0000 Subject: [PATCH 201/255] Optimize --- .../Nethermind.Evm.Benchmark/EvmBenchmarks.cs | 2 +- .../MultipleUnsignedOperations.cs | 2 +- .../StaticCallBenchmarks.cs | 4 +- .../Nethermind.Evm/EvmPooledMemory.cs | 9 ++- .../Nethermind.Evm/IVirtualMachine.cs | 3 +- .../Instructions/EvmInstructions.Call.cs | 10 +-- .../Instructions/EvmInstructions.CodeCopy.cs | 15 ++-- .../Instructions/EvmInstructions.Create.cs | 5 +- .../EvmInstructions.Environment.cs | 15 ++-- .../Instructions/EvmInstructions.Eof.cs | 20 +++--- .../Instructions/EvmInstructions.Storage.cs | 14 ++-- .../Instructions/EvmInstructions.cs | 45 ++++++------ .../TransactionProcessor.cs | 17 ++++- .../Nethermind.Evm/VirtualMachine.cs | 69 ++++++++++--------- .../Simulate/SimulateVirtualMachine.cs | 7 +- 15 files changed, 137 insertions(+), 100 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index b4a5788f78a..6473dc1cf6f 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -64,7 +64,7 @@ public void GlobalSetup() [Benchmark] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.Run(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } } diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs index 185632fb924..f28925f6286 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs @@ -97,7 +97,7 @@ public void GlobalSetup() [Benchmark] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.Run(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs index bce0f7b4800..9cbe402e93f 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs @@ -108,14 +108,14 @@ public void GlobalSetup() [Benchmark(Baseline = true)] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.Run(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } [Benchmark] public void ExecuteCodeNoTracing() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.Run(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } diff --git a/src/Nethermind/Nethermind.Evm/EvmPooledMemory.cs b/src/Nethermind/Nethermind.Evm/EvmPooledMemory.cs index a8cfef56914..cf100141762 100644 --- a/src/Nethermind/Nethermind.Evm/EvmPooledMemory.cs +++ b/src/Nethermind/Nethermind.Evm/EvmPooledMemory.cs @@ -261,10 +261,17 @@ public long CalculateMemoryCost(in UInt256 location, in UInt256 length) long result = CalculateMemoryCost(in location, in length, out bool outOfGas); if (outOfGas) { - throw new OutOfGasException(); + ThrowOutOfGas(); } return result; + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowOutOfGas() + { + throw new OutOfGasException(); + } } public TraceMemory GetTrace() diff --git a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs index 4d404ccee24..6d2dac54688 100644 --- a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs @@ -12,6 +12,7 @@ namespace Nethermind.Evm { public interface IVirtualMachine { - TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer); + TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) + where TTracingInstructions : struct, IFlag; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index b6e07593af1..66eaf7acf8a 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -22,8 +22,9 @@ public interface IOpCall } [SkipLocalsInit] - public static EvmExceptionType InstructionCall(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCall(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCall : struct, IOpCall + where TTracingInstructions : struct, IFlag { Metrics.IncrementCalls(); @@ -110,7 +111,6 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E gasLimitUl += GasCostOf.CallStipend; } - bool tracingInstructions = vm.TxTracer.IsTracingInstructions; if (env.CallDepth >= MaxCallDepth || !transferValue.IsZero && state.GetBalance(env.ExecutingAccount) < transferValue) { @@ -125,14 +125,14 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E } //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); - if (tracingInstructions) + if (TTracingInstructions.IsActive) { vm.TxTracer.ReportOperationRemainingGas(gasAvailable); vm.TxTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); } UpdateGasUp(gasLimitUl, ref gasAvailable); - if (tracingInstructions) + if (TTracingInstructions.IsActive) { vm.TxTracer.ReportGasUpdateForVmTrace(gasLimitUl, gasAvailable); } @@ -142,7 +142,7 @@ public static EvmExceptionType InstructionCall(VirtualMachine vm, ref E Snapshot snapshot = state.TakeSnapshot(); state.SubtractFromBalance(caller, transferValue, spec); - if (codeInfo.IsEmpty && !tracingInstructions && !vm.TxTracer.IsTracingActions) + if (codeInfo.IsEmpty && !TTracingInstructions.IsActive && !vm.TxTracer.IsTracingActions) { // Non contract call, no need to construct call frame can just credit balance and return gas vm.ReturnDataBuffer = default; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 9ad317d5635..736d859eebc 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -18,8 +18,9 @@ public interface IOpCodeCopy } [SkipLocalsInit] - public static EvmExceptionType InstructionCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCodeCopy : struct, IOpCodeCopy + where TTracingInstructions : struct, IFlag { if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 result)) goto StackUnderflow; gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); @@ -30,7 +31,7 @@ public static EvmExceptionType InstructionCodeCopy(VirtualMachine v if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) goto OutOfGas; ZeroPaddedSpan slice = TOpCodeCopy.GetCode(vm).SliceWithZeroPadding(in b, (int)result); vm.EvmState.Memory.Save(in a, in slice); - if (vm.TxTracer.IsTracingInstructions) + if (TTracingInstructions.IsActive) { vm.TxTracer.ReportMemoryChange(a, in slice); } @@ -57,7 +58,8 @@ public static ReadOnlySpan GetCode(VirtualMachine vm) } [SkipLocalsInit] - public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { IReleaseSpec spec = vm.Spec; Address address = stack.PopAddress(); @@ -79,7 +81,7 @@ public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref Evm } ZeroPaddedSpan slice = externalCode.SliceWithZeroPadding(in b, (int)result); vm.EvmState.Memory.Save(in a, in slice); - if (vm.TxTracer.IsTracingInstructions) + if (TTracingInstructions.IsActive) { vm.TxTracer.ReportMemoryChange(a, in slice); } @@ -94,7 +96,8 @@ public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref Evm } [SkipLocalsInit] - public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { IReleaseSpec spec = vm.Spec; gasAvailable -= spec.GetExtCodeCost(); @@ -105,7 +108,7 @@ public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref Evm if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; ReadOnlySpan codeSection = vm.EvmState.Env.CodeInfo.MachineCode.Span; - if (!vm.TxTracer.IsTracingInstructions && programCounter < codeSection.Length) + if (!TTracingInstructions.IsActive && programCounter < codeSection.Length) { bool optimizeAccess = false; Instruction nextInstruction = (Instruction)codeSection[programCounter]; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index cd8560df6e3..38bba3315e4 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -22,8 +22,9 @@ public interface IOpCreate } [SkipLocalsInit] - public static EvmExceptionType InstructionCreate(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCreate(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCreate : struct, IOpCreate + where TTracingInstructions : struct, IFlag { Metrics.IncrementCreates(); @@ -96,7 +97,7 @@ public static EvmExceptionType InstructionCreate(VirtualMachine vm, r goto None; } - if (vm.TxTracer.IsTracingInstructions) vm.EndInstructionTrace(gasAvailable); + if (TTracingInstructions.IsActive) vm.EndInstructionTrace(gasAvailable); // todo: === below is a new call - refactor / move long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index c986182a7db..a7e50f7c17f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -121,7 +121,8 @@ public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref } [SkipLocalsInit] - public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { gasAvailable -= GasCostOf.VeryLow; @@ -129,7 +130,7 @@ public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; Span bytes = vmState.Memory.LoadSpan(in result); - if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange(result, bytes); + if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(result, bytes); stack.PushBytes(bytes); @@ -142,7 +143,8 @@ public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack } [SkipLocalsInit] - public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { gasAvailable -= GasCostOf.VeryLow; @@ -152,7 +154,7 @@ public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; vmState.Memory.SaveWord(in result, bytes); - if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange((long)result, bytes); + if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange((long)result, bytes); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor @@ -163,7 +165,8 @@ public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack } [SkipLocalsInit] - public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { gasAvailable -= GasCostOf.VeryLow; @@ -173,7 +176,7 @@ public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStac EvmState vmState = vm.EvmState; if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in UInt256.One)) goto OutOfGas; vmState.Memory.SaveByte(in result, data); - if (vm.TxTracer.IsTracingInstructions) vm.TxTracer.ReportMemoryChange(result, data); + if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(result, data); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index e02aa9db836..b9ca05bdae7 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -30,7 +30,8 @@ public static EvmExceptionType InstructionReturnDataSize(VirtualMachine vm, ref } [SkipLocalsInit] - public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) goto StackUnderflow; @@ -49,7 +50,7 @@ public static EvmExceptionType InstructionReturnDataCopy(VirtualMachine vm, ref if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, c)) return EvmExceptionType.OutOfGas; ZeroPaddedSpan slice = returnDataBuffer.Span.SliceWithZeroPadding(b, (int)c); vm.EvmState.Memory.Save(in a, in slice); - if (vm.TxTracer.IsTracingInstructions) + if (TTracingInstructions.IsActive) { vm.TxTracer.ReportMemoryChange(a, in slice); } @@ -129,7 +130,8 @@ public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmSta } [SkipLocalsInit] - public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; if (codeInfo.Version == 0) @@ -146,7 +148,7 @@ public static EvmExceptionType InstructionDataCopy(VirtualMachine vm, ref EvmSta goto OutOfGas; ZeroPaddedSpan dataSectionSlice = codeInfo.DataSection.SliceWithZeroPadding(offset, (int)size); vm.EvmState.Memory.Save(in memOffset, dataSectionSlice); - if (vm.TxTracer.IsTracingInstructions) + if (TTracingInstructions.IsActive) { vm.TxTracer.ReportMemoryChange(memOffset, dataSectionSlice); } @@ -402,7 +404,8 @@ public static EvmExceptionType InstructionExchange(VirtualMachine vm, ref EvmSta } [SkipLocalsInit] - public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { Metrics.IncrementCreates(); vm.ReturnData = null; @@ -490,7 +493,7 @@ public static EvmExceptionType InstructionEofCreate(VirtualMachine vm, ref EvmSt } - if (vm.TxTracer.IsTracingInstructions) vm.EndInstructionTrace(gasAvailable); + if (TTracingInstructions.IsActive) vm.EndInstructionTrace(gasAvailable); // todo: === below is a new call - refactor / move Snapshot snapshot = state.TakeSnapshot(); @@ -633,8 +636,9 @@ public struct OpEofStaticCall : IOpEofCall } [SkipLocalsInit] - public static EvmExceptionType InstructionEofCall(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionEofCall(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEofCall : struct, IOpEofCall + where TTracingInstructions : struct, IFlag { Metrics.IncrementCalls(); @@ -725,7 +729,7 @@ public static EvmExceptionType InstructionEofCall(VirtualMachine vm, //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); ITxTracer txTracer = vm.TxTracer; - if (txTracer.IsTracingInstructions) + if (TTracingInstructions.IsActive) { // very specific for Parity trace, need to find generalization - very peculiar 32 length... ReadOnlyMemory memoryTrace = vm.EvmState.Memory.Inspect(in dataOffset, 32); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index f7d030619b6..543a36a0fac 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -79,7 +79,8 @@ public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack } [SkipLocalsInit] - public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { Metrics.MCopyOpcode++; @@ -92,11 +93,9 @@ public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack Span bytes = vmState.Memory.LoadSpan(in b, c); - ITxTracer tracer = !vm.TxTracer.IsTracingInstructions ? null : vm.TxTracer; - - tracer?.ReportMemoryChange(b, bytes); + if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(b, bytes); vmState.Memory.Save(in a, bytes); - tracer?.ReportMemoryChange(a, bytes); + if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(a, bytes); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor @@ -109,7 +108,8 @@ public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack [SkipLocalsInit] [MethodImpl(MethodImplOptions.NoInlining)] - internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag { Metrics.IncrementSStoreOpcode(); EvmState vmState = vm.EvmState; @@ -235,7 +235,7 @@ internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmSta vm.WorldState.Set(in storageCell, newIsZero ? BytesZero : bytes.ToArray()); } - if (vm.TxTracer.IsTracingInstructions) + if (TTracingInstructions.IsActive) { ReadOnlySpan valueToStore = newIsZero ? BytesZero.AsSpan() : bytes; byte[] storageBytes = new byte[32]; // do not stackalloc here diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 1dd7ff6954a..c405348edc1 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -16,7 +16,8 @@ namespace Nethermind.Evm; internal unsafe sealed partial class EvmInstructions { - public static OpCode[] GenerateOpCodes(IReleaseSpec spec) + public static OpCode[] GenerateOpCodes(IReleaseSpec spec) + where TTracingInstructions : struct, IFlag { var lookup = new delegate*[256]; @@ -64,19 +65,19 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.CALLVALUE] = &InstructionEnvUInt256; lookup[(int)Instruction.CALLDATALOAD] = &InstructionCallDataLoad; lookup[(int)Instruction.CALLDATASIZE] = &InstructionEnvUInt256; - lookup[(int)Instruction.CALLDATACOPY] = &InstructionCodeCopy; + lookup[(int)Instruction.CALLDATACOPY] = &InstructionCodeCopy; lookup[(int)Instruction.CODESIZE] = &InstructionEnvUInt256; - lookup[(int)Instruction.CODECOPY] = &InstructionCodeCopy; + lookup[(int)Instruction.CODECOPY] = &InstructionCodeCopy; lookup[(int)Instruction.GASPRICE] = &InstructionEnvUInt256; - lookup[(int)Instruction.EXTCODESIZE] = &InstructionExtCodeSize; + lookup[(int)Instruction.EXTCODESIZE] = &InstructionExtCodeSize; - lookup[(int)Instruction.EXTCODECOPY] = &InstructionExtCodeCopy; + lookup[(int)Instruction.EXTCODECOPY] = &InstructionExtCodeCopy; if (spec.ReturnDataOpcodesEnabled) { lookup[(int)Instruction.RETURNDATASIZE] = &InstructionReturnDataSize; - lookup[(int)Instruction.RETURNDATACOPY] = &InstructionReturnDataCopy; + lookup[(int)Instruction.RETURNDATACOPY] = &InstructionReturnDataCopy; } if (spec.ExtCodeHashOpcodeEnabled) { @@ -112,11 +113,11 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) } // Gap: 0x4b to 0x4f lookup[(int)Instruction.POP] = &InstructionPop; - lookup[(int)Instruction.MLOAD] = &InstructionMLoad; - lookup[(int)Instruction.MSTORE] = &InstructionMStore; - lookup[(int)Instruction.MSTORE8] = &InstructionMStore8; + lookup[(int)Instruction.MLOAD] = &InstructionMLoad; + lookup[(int)Instruction.MSTORE] = &InstructionMStore; + lookup[(int)Instruction.MSTORE8] = &InstructionMStore8; lookup[(int)Instruction.SLOAD] = &InstructionSLoad; - lookup[(int)Instruction.SSTORE] = &InstructionSStore; + lookup[(int)Instruction.SSTORE] = &InstructionSStore; lookup[(int)Instruction.JUMP] = &InstructionJump; lookup[(int)Instruction.JUMPI] = &InstructionJumpIf; lookup[(int)Instruction.PC] = &InstructionProgramCounter; @@ -131,7 +132,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) } if (spec.MCopyIncluded) { - lookup[(int)Instruction.MCOPY] = &InstructionMCopy; + lookup[(int)Instruction.MCOPY] = &InstructionMCopy; } if (spec.IncludePush0Instruction) @@ -217,7 +218,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.DATALOAD] = &InstructionDataLoad; lookup[(int)Instruction.DATALOADN] = &InstructionDataLoadN; lookup[(int)Instruction.DATASIZE] = &InstructionDataSize; - lookup[(int)Instruction.DATACOPY] = &InstructionDataCopy; + lookup[(int)Instruction.DATACOPY] = &InstructionDataCopy; lookup[(int)Instruction.RJUMP] = &InstructionRelativeJump; lookup[(int)Instruction.RJUMPI] = &InstructionRelativeJumpIf; lookup[(int)Instruction.RJUMPV] = &InstructionJumpTable; @@ -227,39 +228,39 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.DUPN] = &InstructionDupN; lookup[(int)Instruction.SWAPN] = &InstructionSwapN; lookup[(int)Instruction.EXCHANGE] = &InstructionExchange; - lookup[(int)Instruction.EOFCREATE] = &InstructionEofCreate; + lookup[(int)Instruction.EOFCREATE] = &InstructionEofCreate; lookup[(int)Instruction.RETURNCONTRACT] = &InstructionReturnContract; } - lookup[(int)Instruction.CREATE] = &InstructionCreate; - lookup[(int)Instruction.CALL] = &InstructionCall; - lookup[(int)Instruction.CALLCODE] = &InstructionCall; + lookup[(int)Instruction.CREATE] = &InstructionCreate; + lookup[(int)Instruction.CALL] = &InstructionCall; + lookup[(int)Instruction.CALLCODE] = &InstructionCall; lookup[(int)Instruction.RETURN] = &InstructionReturn; if (spec.DelegateCallEnabled) { - lookup[(int)Instruction.DELEGATECALL] = &InstructionCall; + lookup[(int)Instruction.DELEGATECALL] = &InstructionCall; } if (spec.Create2OpcodeEnabled) { - lookup[(int)Instruction.CREATE2] = &InstructionCreate; + lookup[(int)Instruction.CREATE2] = &InstructionCreate; } lookup[(int)Instruction.RETURNDATALOAD] = &InstructionReturnDataLoad; if (spec.StaticCallEnabled) { - lookup[(int)Instruction.STATICCALL] = &InstructionCall; + lookup[(int)Instruction.STATICCALL] = &InstructionCall; } if (spec.IsEofEnabled) { - lookup[(int)Instruction.EXTCALL] = &InstructionEofCall; + lookup[(int)Instruction.EXTCALL] = &InstructionEofCall; if (spec.DelegateCallEnabled) { - lookup[(int)Instruction.EXTDELEGATECALL] = &InstructionEofCall; + lookup[(int)Instruction.EXTDELEGATECALL] = &InstructionEofCall; } if (spec.StaticCallEnabled) { - lookup[(int)Instruction.EXTSTATICCALL] = &InstructionEofCall; + lookup[(int)Instruction.EXTSTATICCALL] = &InstructionEofCall; } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 40b73a35efb..add2d2083c6 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -167,7 +167,17 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon long gasAvailable = tx.GasLimit - intrinsicGas.Standard; if (!(result = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, _codeInfoRepository, accessTracker, out ExecutionEnvironment env))) return result; - ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas.FloorGas, accessTracker, gasAvailable, env, out TransactionSubstate? substate, out GasConsumed spentGas, out byte statusCode); + GasConsumed spentGas; + byte statusCode; + TransactionSubstate? substate; + if (!tracer.IsTracingInstructions) + { + ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas.FloorGas, accessTracker, gasAvailable, env, out substate, out spentGas, out statusCode); + } + else + { + ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas.FloorGas, accessTracker, gasAvailable, env, out substate, out spentGas, out statusCode); + } PayFees(tx, header, spec, tracer, substate, spentGas.SpentGas, premiumPerGas, blobBaseFee, statusCode); // Finalize @@ -594,7 +604,7 @@ private TransactionResult BuildExecutionEnvironment( protected virtual bool ShouldValidate(ExecutionOptions opts) => !opts.HasFlag(ExecutionOptions.SkipValidation); - protected virtual void ExecuteEvmCall( + protected virtual void ExecuteEvmCall( Transaction tx, BlockHeader header, IReleaseSpec spec, @@ -608,6 +618,7 @@ protected virtual void ExecuteEvmCall( out TransactionSubstate? substate, out GasConsumed gasConsumed, out byte statusCode) + where TTracingInstructions : struct, IFlag { _ = ShouldValidate(opts); @@ -646,7 +657,7 @@ protected virtual void ExecuteEvmCall( using (EvmState state = EvmState.RentTopLevel(unspentGas, executionType, snapshot, env, accessedItems)) { - substate = VirtualMachine.Run(state, WorldState, tracer); + substate = VirtualMachine.Run(state, WorldState, tracer); unspentGas = state.GasAvailable; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index a440a35ae83..49de5050f1f 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -91,7 +91,7 @@ public sealed unsafe class VirtualMachine : IVirtualMachine private int _sectionIndex; private OpCode[] _opcodeMethods; - private static long _txExecutedTracker; + private static long _txCount; public VirtualMachine( IBlockhashProvider? blockHashProvider, @@ -106,19 +106,27 @@ public VirtualMachine( _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); } - public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) + public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) + where TTracingInstructions : struct, IFlag { _txTracer = txTracer; _state = worldState; _spec = _specProvider.GetSpec(state.Env.TxExecutionContext.BlockExecutionContext.Header.Number, state.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp); - if (_txExecutedTracker < 250_000 && Interlocked.Increment(ref _txExecutedTracker) % 10_000 == 0) + if (!TTracingInstructions.IsActive) + { + if (_txCount < 250_000 && Interlocked.Increment(ref _txCount) % 10_000 == 0) + { + if (_logger.IsDebug) _logger.Debug("Resetting EVM instructions cache"); + // Flush the cache every 10_000 transactions to directly point at any PGO optimized methods rather than via pre-stubs + _spec.EvmInstructions = EvmInstructions.GenerateOpCodes(_spec); + } + _opcodeMethods = (OpCode[])(_spec.EvmInstructions ??= EvmInstructions.GenerateOpCodes(_spec)); + } + else { - if (_logger.IsDebug) _logger.Debug("Resetting EVM instructions cache"); - // Flush the cache every 10_000 transactions to directly point at any PGO optimized methods rather than via pre-stubs - _spec.EvmInstructions = EvmInstructions.GenerateOpCodes(_spec); + _opcodeMethods = EvmInstructions.GenerateOpCodes(_spec); } - _opcodeMethods = (OpCode[])(_spec.EvmInstructions ??= EvmInstructions.GenerateOpCodes(_spec)); ref readonly TxExecutionContext txExecutionContext = ref state.Env.TxExecutionContext; ICodeInfoRepository codeInfoRepository = txExecutionContext.CodeInfoRepository; IReleaseSpec spec = _specProvider.GetSpec(txExecutionContext.BlockExecutionContext.Header.Number, txExecutionContext.BlockExecutionContext.Header.Timestamp); @@ -184,7 +192,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer if (currentState.Env.CodeInfo is not null) { - callResult = ExecuteCall(currentState, previousCallResult, previousCallOutput, previousCallOutputDestination); + callResult = ExecuteCall(currentState, previousCallResult, previousCallOutput, previousCallOutputDestination); } else { @@ -416,7 +424,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer if (previousState.IsPrecompile) { // parity induced if else for vmtrace - if (_txTracer.IsTracingInstructions) + if (TTracingInstructions.IsActive) { _txTracer.ReportMemoryChange(previousCallOutputDestination, previousCallOutput); } @@ -467,7 +475,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer RevertParityTouchBugAccount(); - if (txTracer.IsTracingInstructions) + if (TTracingInstructions.IsActive) { txTracer.ReportOperationRemainingGas(0); txTracer.ReportOperationError(failure is EvmException evmException ? evmException.ExceptionType : EvmExceptionType.Other); @@ -586,7 +594,8 @@ private CallResult ExecutePrecompile(EvmState state) /// values at compile time. /// [SkipLocalsInit] - private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousCallResult, ZeroPaddedSpan previousCallOutput, scoped in UInt256 previousCallOutputDestination) + private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousCallResult, ZeroPaddedSpan previousCallOutput, scoped in UInt256 previousCallOutputDestination) + where TTracingInstructions : struct, IFlag { ref readonly ExecutionEnvironment env = ref vmState.Env; if (!vmState.IsContinuation) @@ -615,7 +624,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousC if (previousCallResult is not null) { stack.PushBytes(previousCallResult.Value.Span); - if (_txTracer.IsTracingInstructions) _txTracer.ReportOperationRemainingGas(vmState.GasAvailable); + if (TTracingInstructions.IsActive) _txTracer.ReportOperationRemainingGas(vmState.GasAvailable); } if (previousCallOutput.Length > 0) @@ -635,12 +644,10 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousC // OnFlag or OffFlag. These checks that are evaluated to constant values at compile time. // This only works for structs, not for classes or interface types // which use shared generics. - return (tracing: _txTracer.IsTracingInstructions, cancelable: _txTracer.IsCancelable) switch + return _txTracer.IsCancelable switch { - (tracing: false, cancelable: false) => ExecuteCode(ref stack, gasAvailable), - (tracing: false, cancelable: true) => ExecuteCode(ref stack, gasAvailable), - (tracing: true, cancelable: false) => ExecuteCode(ref stack, gasAvailable), - (tracing: true, cancelable: true) => ExecuteCode(ref stack, gasAvailable) + false => ExecuteCode(ref stack, gasAvailable), + true => ExecuteCode(ref stack, gasAvailable), }; Empty: return CallResult.Empty(vmState.Env.CodeInfo.Version); @@ -820,20 +827,6 @@ private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExc _txTracer.ReportOperationError(evmExceptionType); } - interface IFlag - { - virtual static bool IsActive { get; } - } - - struct OffFlag : IFlag - { - public static bool IsActive => false; - } - struct OnFlag : IFlag - { - public static bool IsActive => true; - } - internal readonly ref struct CallResult { public static CallResult InvalidSubroutineEntry => new(EvmExceptionType.InvalidSubroutineEntry); @@ -897,3 +890,17 @@ private CallResult(EvmExceptionType exceptionType) public int FromVersion { get; } } } + +public interface IFlag +{ + virtual static bool IsActive { get; } +} + +public struct OffFlag : IFlag +{ + public static bool IsActive => false; +} +public struct OnFlag : IFlag +{ + public static bool IsActive => true; +} diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs index 08a7d388031..9e79baff0ef 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Diagnostics.CodeAnalysis; - -using Nethermind.Consensus.Tracing; using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.State; @@ -12,14 +10,15 @@ namespace Nethermind.Facade.Simulate; public class SimulateVirtualMachine(IVirtualMachine virtualMachine) : IVirtualMachine { - public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) + public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) + where TTracingInstructions : struct, IFlag { if (txTracer.IsTracingActions && TryGetLogsMutator(txTracer, out ITxLogsMutator logsMutator)) { logsMutator.SetLogsToMutate(state.AccessTracker.Logs); } - return virtualMachine.Run(state, worldState, txTracer); + return virtualMachine.Run(state, worldState, txTracer); } private static bool TryGetLogsMutator(ITxTracer txTracer, [NotNullWhen(true)] out ITxLogsMutator? txLogsMutator) From 524fe8c41683263e0f03760b9c45c261997ac570 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 2 Feb 2025 21:56:35 +0000 Subject: [PATCH 202/255] Optimize --- src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs | 2 ++ .../Nethermind.Core/Specs/ReleaseSpecDecorator.cs | 1 + src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 6 ++++-- .../Nethermind.Specs.Test/OverridableReleaseSpec.cs | 1 + src/Nethermind/Nethermind.Specs/ReleaseSpec.cs | 1 + 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index 48b29624cc1..dcd4d76164a 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -439,5 +439,7 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec public bool RequestsEnabled => ConsolidationRequestsEnabled || WithdrawalRequestsEnabled || DepositsEnabled; public object? EvmInstructions { get; set; } + + public object? EvmTracedInstructions { get; set; } } } diff --git a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs index 455cc90de3a..4045861d26e 100644 --- a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs +++ b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs @@ -140,4 +140,5 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec public virtual UInt256? Eip1559BaseFeeMinValue => spec.Eip1559BaseFeeMinValue; public virtual bool ValidateReceipts => spec.ValidateReceipts; public object? EvmInstructions { get => spec.EvmInstructions; set => spec.EvmInstructions = value; } + public object? EvmTracedInstructions { get => spec.EvmTracedInstructions; set => spec.EvmTracedInstructions = value; } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 49de5050f1f..4dc3aaed7c5 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -115,17 +115,19 @@ public TransactionSubstate Run(EvmState state, IWorldState _spec = _specProvider.GetSpec(state.Env.TxExecutionContext.BlockExecutionContext.Header.Number, state.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp); if (!TTracingInstructions.IsActive) { - if (_txCount < 250_000 && Interlocked.Increment(ref _txCount) % 10_000 == 0) + if (_txCount < 500_000 && Interlocked.Increment(ref _txCount) % 10_000 == 0) { if (_logger.IsDebug) _logger.Debug("Resetting EVM instructions cache"); // Flush the cache every 10_000 transactions to directly point at any PGO optimized methods rather than via pre-stubs + // May be a few cycles to pick up pointers to the optimized methods depending on what's in the blocks, + // however the the refreshes don't take long. _spec.EvmInstructions = EvmInstructions.GenerateOpCodes(_spec); } _opcodeMethods = (OpCode[])(_spec.EvmInstructions ??= EvmInstructions.GenerateOpCodes(_spec)); } else { - _opcodeMethods = EvmInstructions.GenerateOpCodes(_spec); + _opcodeMethods = (OpCode[])(_spec.EvmTracedInstructions ??= EvmInstructions.GenerateOpCodes(_spec)); } ref readonly TxExecutionContext txExecutionContext = ref state.Env.TxExecutionContext; ICodeInfoRepository codeInfoRepository = txExecutionContext.CodeInfoRepository; diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 9f8af92780d..f25960f94d1 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -186,5 +186,6 @@ public ulong Eip4844TransitionTimestamp public Address DepositContractAddress => _spec.DepositContractAddress; public object? EvmInstructions { get => _spec.EvmInstructions; set => _spec.EvmInstructions = value; } + public object? EvmTracedInstructions { get => _spec.EvmTracedInstructions; set => _spec.EvmTracedInstructions = value; } } } diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index 7e4fecbd3aa..47c72d8f63c 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -141,5 +141,6 @@ public Address Eip2935ContractAddress set => _eip2935ContractAddress = value; } public object EvmInstructions { get; set; } + public object? EvmTracedInstructions { get; set; } } } From 9cef3ef0507ef55a14dfd317ec0b4a61f858676f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 2 Feb 2025 22:26:53 +0000 Subject: [PATCH 203/255] Optimize --- .../Nethermind.Evm/EvmPooledMemory.cs | 1 + .../Instructions/EvmInstructions.Eof.cs | 4 ++-- .../Instructions/EvmInstructions.Storage.cs | 19 +++++++++++-------- .../Instructions/EvmInstructions.cs | 3 +++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmPooledMemory.cs b/src/Nethermind/Nethermind.Evm/EvmPooledMemory.cs index cf100141762..134f9c5916e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmPooledMemory.cs +++ b/src/Nethermind/Nethermind.Evm/EvmPooledMemory.cs @@ -291,6 +291,7 @@ public void Dispose() } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long Div32Ceiling(in UInt256 length, out bool outOfGas) { if (length.IsLargerThanULong()) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index b9ca05bdae7..945ab217803 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -411,13 +411,13 @@ public static EvmExceptionType InstructionEofCreate(Virtua vm.ReturnData = null; IReleaseSpec spec = vm.Spec; - ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; + ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; + ICodeInfo codeInfo = env.CodeInfo; if (codeInfo.Version == 0) goto BadInstruction; if (vm.EvmState.IsStatic) goto StaticCallViolation; - ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; EofCodeInfo container = env.CodeInfo as EofCodeInfo; ExecutionType currentContext = ExecutionType.EOFCREATE; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index 543a36a0fac..da89dbe680d 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -107,7 +107,6 @@ public static EvmExceptionType InstructionMCopy(VirtualMac [SkipLocalsInit] - [MethodImpl(MethodImplOptions.NoInlining)] internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag { @@ -259,7 +258,6 @@ internal static EvmExceptionType InstructionSStore(Virtual } [SkipLocalsInit] - [MethodImpl(MethodImplOptions.NoInlining)] internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; @@ -267,19 +265,23 @@ internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStac gasAvailable -= spec.GetSLoadCost(); if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; - StorageCell storageCell = new(vm.EvmState.Env.ExecutingAccount, result); + Address executingAccount = vm.EvmState.Env.ExecutingAccount; + StorageCell storageCell = new(executingAccount, result); if (!ChargeStorageAccessGas( ref gasAvailable, vm, in storageCell, StorageAccessType.SLOAD, - spec)) goto OutOfGas; + spec)) + { + goto OutOfGas; + } ReadOnlySpan value = vm.WorldState.Get(in storageCell); stack.PushBytes(value); if (vm.TxTracer.IsTracingStorage) { - vm.TxTracer.LoadOperationStorage(storageCell.Address, result, value); + vm.TxTracer.LoadOperationStorage(executingAccount, result, value); } return EvmExceptionType.None; @@ -301,15 +303,16 @@ internal static bool ChargeStorageAccessGas( bool result = true; if (spec.UseHotAndColdStorage) { + ref readonly StackAccessTracker accessTracker = ref vmState.AccessTracker; if (vm.TxTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list { - vmState.AccessTracker.WarmUp(in storageCell); + accessTracker.WarmUp(in storageCell); } - if (vmState.AccessTracker.IsCold(in storageCell)) + if (accessTracker.IsCold(in storageCell)) { result = UpdateGas(GasCostOf.ColdSLoad, ref gasAvailable); - vmState.AccessTracker.WarmUp(in storageCell); + accessTracker.WarmUp(in storageCell); } else if (storageAccessType == StorageAccessType.SLOAD) { diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index c405348edc1..925ac4dbed7 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -510,6 +510,7 @@ public static EvmExceptionType InstructionCallDataLoad(VirtualMachine vm, ref Ev return EvmExceptionType.None; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) { long memoryCost = vmState.Memory.CalculateMemoryCost(in position, length); @@ -524,6 +525,7 @@ public static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool UpdateGas(long gasCost, ref long gasAvailable) { if (gasAvailable < gasCost) @@ -535,6 +537,7 @@ public static bool UpdateGas(long gasCost, ref long gasAvailable) return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UpdateGasUp(long refund, ref long gasAvailable) { gasAvailable += refund; From 6f4987a35cc43e638e0b1524dfcf9b20074d99a5 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 2 Feb 2025 23:49:23 +0000 Subject: [PATCH 204/255] Optimization --- src/Nethermind/Nethermind.Evm/EvmStack.cs | 1 + .../EvmInstructions.Environment.cs | 1 + .../Nethermind.Evm/VirtualMachine.cs | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index de01bd9f9df..349a0d2a680 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -201,6 +201,7 @@ public void PushSignedInt256(in Int256.Int256 value) PushUInt256(in Unsafe.As(ref Unsafe.AsRef(in value))); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool PopLimbo() { if (Head-- == 0) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index a7e50f7c17f..dcb29b03d79 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -19,6 +19,7 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { [SkipLocalsInit] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EvmExceptionType InstructionPop(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 4dc3aaed7c5..96239a59327 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -694,11 +694,19 @@ private unsafe CallResult ExecuteCode(scoped // Advance the program counter one instruction programCounter++; - // Get the opcode delegate* from the opcode array - OpCode opcodeMethod = opcodeMethods[(int)instruction]; - // Execute opcode delegate* via calli (see: C# function pointers https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers) - // Stack, gas, and program counter may be modified by call (also instance variables on the vm) - exceptionType = opcodeMethod(this, ref stack, ref gasAvailable, ref programCounter); + if (Instruction.POP == instruction) + { + // Very commonly called opcode and minimal implementation, so we inline here + exceptionType = EvmInstructions.InstructionPop(this, ref stack, ref gasAvailable, ref programCounter); + } + else + { + // Get the opcode delegate* from the opcode array + OpCode opcodeMethod = (OpCode)Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(opcodeMethods)), (int)instruction); + // Execute opcode delegate* via calli (see: C# function pointers https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers) + // Stack, gas, and program counter may be modified by call (also instance variables on the vm) + exceptionType = opcodeMethod(this, ref stack, ref gasAvailable, ref programCounter); + } // Exit loop if run out of gas if (gasAvailable < 0) goto OutOfGas; From 6846c13082055fedda86c2da9d9a35ea53fef664 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 00:03:47 +0000 Subject: [PATCH 205/255] Optimize --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 96239a59327..66e9df6bd9a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -673,7 +673,7 @@ private unsafe CallResult ExecuteCode(scoped DebugTracer? debugger = _txTracer.GetTracer(); #endif - OpCode[] opcodeMethods = _opcodeMethods; + ref nuint opcodeMethods = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(_opcodeMethods)); // Initialize program counter to the current state's value. // Entry point is not always 0 as we may be returning to code after a call. int programCounter = _vmState.ProgramCounter; @@ -702,7 +702,7 @@ private unsafe CallResult ExecuteCode(scoped else { // Get the opcode delegate* from the opcode array - OpCode opcodeMethod = (OpCode)Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(opcodeMethods)), (int)instruction); + OpCode opcodeMethod = (OpCode)Unsafe.Add(ref opcodeMethods, (int)instruction); // Execute opcode delegate* via calli (see: C# function pointers https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers) // Stack, gas, and program counter may be modified by call (also instance variables on the vm) exceptionType = opcodeMethod(this, ref stack, ref gasAvailable, ref programCounter); From d14ea2950c7be2e11eff0487a2197074f7387120 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 01:26:25 +0000 Subject: [PATCH 206/255] Optimize --- .../Nethermind.Evm/VirtualMachine.cs | 68 ++++++++++--------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 66e9df6bd9a..8789c8be653 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -673,50 +673,52 @@ private unsafe CallResult ExecuteCode(scoped DebugTracer? debugger = _txTracer.GetTracer(); #endif - ref nuint opcodeMethods = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(_opcodeMethods)); // Initialize program counter to the current state's value. // Entry point is not always 0 as we may be returning to code after a call. int programCounter = _vmState.ProgramCounter; - // We use a while loop rather than a for loop as some - // opcodes can change the program counter (e.g. Push, Jump, etc) - while ((uint)programCounter < (uint)codeSection.Length) + fixed (OpCode* opcodeMethods = &_opcodeMethods[0]) { + // We use a while loop rather than a for loop as some + // opcodes can change the program counter (e.g. Push, Jump, etc) + while ((uint)programCounter < (uint)codeSection.Length) + { #if DEBUG - debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); + debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); #endif - // Get the opcode at the current program counter - Instruction instruction = codeSection[programCounter]; + // Get the opcode at the current program counter + Instruction instruction = codeSection[programCounter]; - if (TCancelable.IsActive && _txTracer.IsCancelled) ThrowOperationCanceledException(); - if (TTracingInstructions.IsActive) - StartInstructionTrace(instruction, gasAvailable, programCounter, in stack); + if (TCancelable.IsActive && _txTracer.IsCancelled) ThrowOperationCanceledException(); + if (TTracingInstructions.IsActive) + StartInstructionTrace(instruction, gasAvailable, programCounter, in stack); - // Advance the program counter one instruction - programCounter++; + // Advance the program counter one instruction + programCounter++; - if (Instruction.POP == instruction) - { - // Very commonly called opcode and minimal implementation, so we inline here - exceptionType = EvmInstructions.InstructionPop(this, ref stack, ref gasAvailable, ref programCounter); - } - else - { - // Get the opcode delegate* from the opcode array - OpCode opcodeMethod = (OpCode)Unsafe.Add(ref opcodeMethods, (int)instruction); - // Execute opcode delegate* via calli (see: C# function pointers https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers) - // Stack, gas, and program counter may be modified by call (also instance variables on the vm) - exceptionType = opcodeMethod(this, ref stack, ref gasAvailable, ref programCounter); - } + if (Instruction.POP == instruction) + { + // Very commonly called opcode and minimal implementation, so we inline here + exceptionType = EvmInstructions.InstructionPop(this, ref stack, ref gasAvailable, ref programCounter); + } + else + { + // Get the opcode delegate* from the opcode array + OpCode opcodeMethod = opcodeMethods[(int)instruction]; + // Execute opcode delegate* via calli (see: C# function pointers https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers) + // Stack, gas, and program counter may be modified by call (also instance variables on the vm) + exceptionType = opcodeMethod(this, ref stack, ref gasAvailable, ref programCounter); + } - // Exit loop if run out of gas - if (gasAvailable < 0) goto OutOfGas; - // Exit loop if exception occurred - if (exceptionType != EvmExceptionType.None) break; - // Exit loop if returning data - if (_returnData is not null) break; + // Exit loop if run out of gas + if (gasAvailable < 0) goto OutOfGas; + // Exit loop if exception occurred + if (exceptionType != EvmExceptionType.None) break; + // Exit loop if returning data + if (_returnData is not null) break; - if (TTracingInstructions.IsActive) - EndInstructionTrace(gasAvailable); + if (TTracingInstructions.IsActive) + EndInstructionTrace(gasAvailable); + } } if (exceptionType is EvmExceptionType.None or EvmExceptionType.Stop or EvmExceptionType.Revert) From 4ee95fd67d0c711309baab81bfb60b542e79f7e0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 01:31:16 +0000 Subject: [PATCH 207/255] Add comment --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8789c8be653..5558bf7bf7a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -676,6 +676,8 @@ private unsafe CallResult ExecuteCode(scoped // Initialize program counter to the current state's value. // Entry point is not always 0 as we may be returning to code after a call. int programCounter = _vmState.ProgramCounter; + // Use fixed pointer or we loose the type when trying skip bounds check, + // and have to cast for each call (delegate*<...> can't be used as a generic arg) fixed (OpCode* opcodeMethods = &_opcodeMethods[0]) { // We use a while loop rather than a for loop as some From 20cfe69c113b130fc6cdfba26c873821a9240e4d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 04:33:52 +0000 Subject: [PATCH 208/255] Optimize --- src/Nethermind/Nethermind.Evm/EvmStack.cs | 33 ++++++++++++ .../Instructions/EvmInstructions.Stack.cs | 51 ++++++++++++++++--- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 349a0d2a680..fa4c5d60220 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -17,6 +17,7 @@ namespace Nethermind.Evm; using Word = Vector256; +using HalfWord = Vector128; [StructLayout(LayoutKind.Auto)] public ref struct EvmStack @@ -86,6 +87,35 @@ public void PushBytes(scoped in ZeroPaddedSpan value) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PushWord(in Word value) + { + if (_tracer is not null) TraceWord(in value); + + ref byte bytes = ref PushBytesRef(); + Unsafe.As(ref bytes) = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push20Bytes(ref byte value) + { + if (_tracer is not null) Trace20Bytes(in value); + + ref byte bytes = ref PushBytesRef(); + + // First 4+8 bytes are zero + Unsafe.As(ref bytes) = 0; + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(ulong))) = 0; + + // 20 bytes which is uint+Vector128 + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(uint) + sizeof(ulong))) + = Unsafe.As(ref value); + + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(ulong) + sizeof(ulong))) + = Unsafe.As(ref Unsafe.Add(ref value, sizeof(uint))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PushLeftPaddedBytes(ReadOnlySpan value, int paddingLength) { _tracer?.ReportStackPush(value); @@ -103,6 +133,7 @@ public void PushLeftPaddedBytes(ReadOnlySpan value, int paddingLength) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PushByte(byte value) { _tracer?.ReportStackPush(value); @@ -427,6 +458,8 @@ private readonly void Trace(int depth) } } + private readonly void TraceWord(in Word value) => _tracer?.ReportStackPush(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(in value, 1))); + private readonly void Trace20Bytes(in byte value) => _tracer?.ReportStackPush(MemoryMarshal.CreateReadOnlySpan(in value, 20)); [StackTraceHidden] [DoesNotReturn] diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index e16b6f75758..1f2f5c71251 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -3,8 +3,10 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Nethermind.Core; using Nethermind.Core.Crypto; +using Word = System.Runtime.Intrinsics.Vector256; namespace Nethermind.Evm; using Int256; @@ -14,6 +16,11 @@ internal sealed partial class EvmInstructions public interface IOpCount { abstract static int Count { get; } + virtual static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) + { + int usedFromCode = Math.Min(code.Length - programCounter, length); + stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + } } public struct Op0 : IOpCount { public static int Count => 0; } @@ -36,7 +43,25 @@ public struct Op16 : IOpCount { public static int Count => 16; } public struct Op17 : IOpCount { public static int Count => 17; } public struct Op18 : IOpCount { public static int Count => 18; } public struct Op19 : IOpCount { public static int Count => 19; } - public struct Op20 : IOpCount { public static int Count => 20; } + public struct Op20 : IOpCount + { + const int Size = 20; + public static int Count => Size; + public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) + { + int usedFromCode = Math.Min(code.Length - programCounter, length); + if (usedFromCode == Size) + { + // Address + ref byte bytes = ref MemoryMarshal.GetReference(code); + stack.Push20Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + } + else + { + stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + } + } + } public struct Op21 : IOpCount { public static int Count => 21; } public struct Op22 : IOpCount { public static int Count => 22; } public struct Op23 : IOpCount { public static int Count => 23; } @@ -48,7 +73,23 @@ public struct Op28 : IOpCount { public static int Count => 28; } public struct Op29 : IOpCount { public static int Count => 29; } public struct Op30 : IOpCount { public static int Count => 30; } public struct Op31 : IOpCount { public static int Count => 31; } - public struct Op32 : IOpCount { public static int Count => 32; } + public struct Op32 : IOpCount + { + const int Size = 32; + public static int Count => Size; + public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) + { + int usedFromCode = Math.Min(code.Length - programCounter, length); + if (usedFromCode == Size) + { + stack.PushWord(in Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter))); + } + else + { + stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + } + } + } [SkipLocalsInit] public static EvmExceptionType InstructionDup(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) @@ -94,11 +135,9 @@ public static EvmExceptionType InstructionPush(VirtualMachine vm, ref ReadOnlySpan code = vm.EvmState.Env.CodeInfo.CodeSection.Span; - int length = TOpCount.Count; - int usedFromCode = Math.Min(code.Length - programCounter, length); - stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + TOpCount.Push(TOpCount.Count, ref stack, programCounter, code); - programCounter += length; + programCounter += TOpCount.Count; return EvmExceptionType.None; } From 5fbc8cc9206b77518a75f855bcee223f232b874e Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 04:44:54 +0000 Subject: [PATCH 209/255] Optimize --- .../Instructions/EvmInstructions.Stack.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index 1f2f5c71251..c5b77769d99 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -24,7 +24,25 @@ virtual static void Push(int length, ref EvmStack stack, int programCounter, Rea } public struct Op0 : IOpCount { public static int Count => 0; } - public struct Op1 : IOpCount { public static int Count => 1; } + public struct Op1 : IOpCount + { + const int Size = 1; + public static int Count => Size; + public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) + { + int usedFromCode = Math.Min(code.Length - programCounter, length); + if (usedFromCode == Size) + { + // Single byte + ref byte bytes = ref MemoryMarshal.GetReference(code); + stack.PushByte(Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + } + else + { + stack.PushZero(); + } + } + } public struct Op2 : IOpCount { public static int Count => 2; } public struct Op3 : IOpCount { public static int Count => 3; } public struct Op4 : IOpCount { public static int Count => 4; } From ac05b5a2fde3ca47a63e2fdbd2b204ced99286ff Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 04:47:57 +0000 Subject: [PATCH 210/255] Optimize --- .../Instructions/EvmInstructions.Stack.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index c5b77769d99..556ddacb3dc 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -16,6 +16,8 @@ internal sealed partial class EvmInstructions public interface IOpCount { abstract static int Count { get; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] virtual static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { int usedFromCode = Math.Min(code.Length - programCounter, length); @@ -24,10 +26,12 @@ virtual static void Push(int length, ref EvmStack stack, int programCounter, Rea } public struct Op0 : IOpCount { public static int Count => 0; } - public struct Op1 : IOpCount + public struct Op1 : IOpCount { const int Size = 1; public static int Count => Size; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { int usedFromCode = Math.Min(code.Length - programCounter, length); @@ -65,6 +69,8 @@ public struct Op20 : IOpCount { const int Size = 20; public static int Count => Size; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { int usedFromCode = Math.Min(code.Length - programCounter, length); @@ -95,6 +101,8 @@ public struct Op32 : IOpCount { const int Size = 32; public static int Count => Size; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { int usedFromCode = Math.Min(code.Length - programCounter, length); From 575b8996a5053c78b29c7cfdfb3c0c7add0009e9 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 12:46:49 +0000 Subject: [PATCH 211/255] Optimize --- src/Nethermind/Nethermind.Evm/EvmStack.cs | 100 +++++++++++++++--- .../Instructions/EvmInstructions.Stack.cs | 86 ++++++++++++++- 2 files changed, 165 insertions(+), 21 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index fa4c5d60220..f77575e22cc 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -88,18 +88,88 @@ public void PushBytes(scoped in ZeroPaddedSpan value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PushWord(in Word value) + public void PushByte(byte value) { - if (_tracer is not null) TraceWord(in value); + _tracer?.ReportStackPush(value); ref byte bytes = ref PushBytesRef(); - Unsafe.As(ref bytes) = value; + // Not full entry, clear first + Unsafe.As(ref bytes) = default; + Unsafe.Add(ref bytes, WordSize - sizeof(byte)) = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void Push2Bytes(ref byte value) + { + // ushort size + if (_tracer is not null) TraceBytes(in value, 2); + + ref byte bytes = ref PushBytesRef(); + + // Clear 32 bytes + Unsafe.As(ref bytes) = default; + + // Copy 2 bytes + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord) + sizeof(ulong) + sizeof(uint) + sizeof(ushort))) + = Unsafe.As(ref value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void Push4Bytes(ref byte value) + { + // uint size + if (_tracer is not null) TraceBytes(in value, 4); + + ref byte bytes = ref PushBytesRef(); + + // First 16+8+4 bytes are zero + Unsafe.As(ref bytes) = default; + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord))) = default; + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord) + sizeof(ulong))) = default; + + // Copy 4 bytes + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord) + sizeof(ulong) + sizeof(uint))) + = Unsafe.As(ref value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void Push8Bytes(ref byte value) + { + // ulong size + if (_tracer is not null) TraceBytes(in value, 8); + + ref byte bytes = ref PushBytesRef(); + + // First 16+8 bytes are zero + Unsafe.As(ref bytes) = default; + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord))) = default; + + // Copy 8 bytes + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord) + sizeof(ulong))) + = Unsafe.As(ref value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void Push16Bytes(ref byte value) + { + // UInt16 size + if (_tracer is not null) TraceBytes(in value, 16); + + ref byte bytes = ref PushBytesRef(); + + // First 16 bytes are zero + Unsafe.As(ref bytes) = default; + + // Copy 16 bytes + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord))) + = Unsafe.As(ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Push20Bytes(ref byte value) { - if (_tracer is not null) Trace20Bytes(in value); + // Address size + if (_tracer is not null) TraceBytes(in value, 20); ref byte bytes = ref PushBytesRef(); @@ -115,6 +185,15 @@ public void Push20Bytes(ref byte value) = Unsafe.As(ref Unsafe.Add(ref value, sizeof(uint))); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push32Bytes(in Word value) + { + if (_tracer is not null) TraceWord(in value); + + ref byte bytes = ref PushBytesRef(); + Unsafe.As(ref bytes) = value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PushLeftPaddedBytes(ReadOnlySpan value, int paddingLength) { @@ -133,17 +212,6 @@ public void PushLeftPaddedBytes(ReadOnlySpan value, int paddingLength) } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PushByte(byte value) - { - _tracer?.ReportStackPush(value); - - ref byte bytes = ref PushBytesRef(); - // Not full entry, clear first - Unsafe.As(ref bytes) = default; - Unsafe.Add(ref bytes, WordSize - sizeof(byte)) = value; - } - public void PushOne() { _tracer?.ReportStackPush(Bytes.OneByteSpan); @@ -459,7 +527,7 @@ private readonly void Trace(int depth) } private readonly void TraceWord(in Word value) => _tracer?.ReportStackPush(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(in value, 1))); - private readonly void Trace20Bytes(in byte value) => _tracer?.ReportStackPush(MemoryMarshal.CreateReadOnlySpan(in value, 20)); + private readonly void TraceBytes(in byte value, int length) => _tracer?.ReportStackPush(MemoryMarshal.CreateReadOnlySpan(in value, length)); [StackTraceHidden] [DoesNotReturn] diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index 556ddacb3dc..1d984023784 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -47,13 +47,70 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read } } } - public struct Op2 : IOpCount { public static int Count => 2; } + public struct Op2 : IOpCount + { + const int Size = 2; + public static int Count => Size; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) + { + int usedFromCode = Math.Min(code.Length - programCounter, length); + if (usedFromCode == Size) + { + ref byte bytes = ref MemoryMarshal.GetReference(code); + stack.Push2Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + } + else + { + stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + } + } + } public struct Op3 : IOpCount { public static int Count => 3; } - public struct Op4 : IOpCount { public static int Count => 4; } + public struct Op4 : IOpCount + { + const int Size = 4; + public static int Count => Size; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) + { + int usedFromCode = Math.Min(code.Length - programCounter, length); + if (usedFromCode == Size) + { + ref byte bytes = ref MemoryMarshal.GetReference(code); + stack.Push4Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + } + else + { + stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + } + } + } public struct Op5 : IOpCount { public static int Count => 5; } public struct Op6 : IOpCount { public static int Count => 6; } public struct Op7 : IOpCount { public static int Count => 7; } - public struct Op8 : IOpCount { public static int Count => 8; } + public struct Op8 : IOpCount + { + const int Size = 8; + public static int Count => Size; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) + { + int usedFromCode = Math.Min(code.Length - programCounter, length); + if (usedFromCode == Size) + { + ref byte bytes = ref MemoryMarshal.GetReference(code); + stack.Push8Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + } + else + { + stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + } + } + } public struct Op9 : IOpCount { public static int Count => 9; } public struct Op10 : IOpCount { public static int Count => 10; } public struct Op11 : IOpCount { public static int Count => 11; } @@ -61,7 +118,26 @@ public struct Op12 : IOpCount { public static int Count => 12; } public struct Op13 : IOpCount { public static int Count => 13; } public struct Op14 : IOpCount { public static int Count => 14; } public struct Op15 : IOpCount { public static int Count => 15; } - public struct Op16 : IOpCount { public static int Count => 16; } + public struct Op16 : IOpCount + { + const int Size = 16; + public static int Count => Size; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) + { + int usedFromCode = Math.Min(code.Length - programCounter, length); + if (usedFromCode == Size) + { + ref byte bytes = ref MemoryMarshal.GetReference(code); + stack.Push16Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + } + else + { + stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); + } + } + } public struct Op17 : IOpCount { public static int Count => 17; } public struct Op18 : IOpCount { public static int Count => 18; } public struct Op19 : IOpCount { public static int Count => 19; } @@ -108,7 +184,7 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read int usedFromCode = Math.Min(code.Length - programCounter, length); if (usedFromCode == Size) { - stack.PushWord(in Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter))); + stack.Push32Bytes(in Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter))); } else { From 67153f3139b5c0653329c8277b4403b89f6ab093 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 22:01:43 +0000 Subject: [PATCH 212/255] Fix failing tests --- .../TransactionProcessor.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index add2d2083c6..0796981c886 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -19,9 +19,7 @@ using Nethermind.Logging; using Nethermind.State; using Nethermind.State.Tracing; -using static Nethermind.Core.Extensions.MemoryExtensions; using static Nethermind.Evm.EvmObjectFormat.EofValidator; -using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm.TransactionProcessing { @@ -172,11 +170,11 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon TransactionSubstate? substate; if (!tracer.IsTracingInstructions) { - ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas.FloorGas, accessTracker, gasAvailable, env, out substate, out spentGas, out statusCode); + ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas, accessTracker, gasAvailable, env, out substate, out spentGas, out statusCode); } else { - ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas.FloorGas, accessTracker, gasAvailable, env, out substate, out spentGas, out statusCode); + ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas, accessTracker, gasAvailable, env, out substate, out spentGas, out statusCode); } PayFees(tx, header, spec, tracer, substate, spentGas.SpentGas, premiumPerGas, blobBaseFee, statusCode); @@ -611,7 +609,7 @@ protected virtual void ExecuteEvmCall( ITxTracer tracer, ExecutionOptions opts, int delegationRefunds, - long floorGas, + IntrinsicGas gas, in StackAccessTracker accessedItems, in long gasAvailable, in ExecutionEnvironment env, @@ -649,8 +647,11 @@ protected virtual void ExecuteEvmCall( { // 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. - gasConsumed = floorGas; - goto Fail; + gasConsumed = gas.MinimalGas; + // If noValidation we didn't charge for gas, so do not refund; otherwise return unspent gas + if (!opts.HasFlag(ExecutionOptions.SkipValidation)) + WorldState.AddToBalance(tx.SenderAddress!, (ulong)(tx.GasLimit - gas.MinimalGas) * env.TxExecutionContext.GasPrice, spec); + goto Complete; } ExecutionType executionType = tx.IsContractCreation ? (tx.IsEofContractCreation ? ExecutionType.TXCREATE : ExecutionType.CREATE) : ExecutionType.TRANSACTION; @@ -762,7 +763,7 @@ protected virtual void ExecuteEvmCall( } gasConsumed = Refund(tx, header, spec, opts, substate, unspentGas, - env.TxExecutionContext.GasPrice, delegationRefunds, floorGas); + env.TxExecutionContext.GasPrice, delegationRefunds, gas.FloorGas); goto Complete; } catch (Exception ex) when (ex is EvmException or OverflowException) // TODO: OverflowException? still needed? hope not From 5edcbed30972eb149d9bb596597b6c4409d40e2a Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 3 Feb 2025 22:45:16 +0000 Subject: [PATCH 213/255] Use KeccakCache --- .../Nethermind.Evm/Instructions/EvmInstructions.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 925ac4dbed7..9a176f2345b 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Numerics; +using System.Runtime.Intrinsics; using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -489,7 +491,8 @@ public static EvmExceptionType InstructionKeccak256(VirtualMachine vm, ref EvmSt if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; Span bytes = vmState.Memory.LoadSpan(in a, b); - stack.PushBytes(ValueKeccak.Compute(bytes).BytesAsSpan); + KeccakCache.ComputeTo(bytes, out ValueHash256 keccak); + stack.Push32Bytes(in Unsafe.As>(ref keccak)); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor From c4f211f04b329cef8703b25c3ebe66dceefb1771 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 00:40:18 +0000 Subject: [PATCH 214/255] Optimize --- src/Nethermind/Nethermind.Evm/EvmStack.cs | 60 +++++-- .../Instructions/EvmInstructions.Bitwise.cs | 26 ++-- .../Instructions/EvmInstructions.Call.cs | 2 +- .../Instructions/EvmInstructions.CodeCopy.cs | 7 +- .../Instructions/EvmInstructions.Create.cs | 2 +- .../EvmInstructions.Environment.cs | 146 +++++++----------- .../Instructions/EvmInstructions.Eof.cs | 7 +- .../Instructions/EvmInstructions.Extras.cs | 2 +- .../Instructions/EvmInstructions.Jump.cs | 4 +- .../EvmInstructions.Math1Param.cs | 18 ++- .../EvmInstructions.Math2Param.cs | 2 +- .../EvmInstructions.Math3Param.cs | 2 +- .../Instructions/EvmInstructions.Shifts.cs | 2 +- .../Instructions/EvmInstructions.Stack.cs | 13 +- .../Instructions/EvmInstructions.Storage.cs | 70 ++++++++- .../Instructions/EvmInstructions.cs | 14 +- 16 files changed, 228 insertions(+), 149 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index f77575e22cc..58d2ee745e7 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -102,7 +102,7 @@ public void PushByte(byte value) public unsafe void Push2Bytes(ref byte value) { // ushort size - if (_tracer is not null) TraceBytes(in value, 2); + if (_tracer is not null) TraceBytes(in value, sizeof(ushort)); ref byte bytes = ref PushBytesRef(); @@ -118,7 +118,7 @@ public unsafe void Push2Bytes(ref byte value) public unsafe void Push4Bytes(ref byte value) { // uint size - if (_tracer is not null) TraceBytes(in value, 4); + if (_tracer is not null) TraceBytes(in value, sizeof(uint)); ref byte bytes = ref PushBytesRef(); @@ -136,7 +136,7 @@ public unsafe void Push4Bytes(ref byte value) public unsafe void Push8Bytes(ref byte value) { // ulong size - if (_tracer is not null) TraceBytes(in value, 8); + if (_tracer is not null) TraceBytes(in value, sizeof(ulong)); ref byte bytes = ref PushBytesRef(); @@ -152,8 +152,8 @@ public unsafe void Push8Bytes(ref byte value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void Push16Bytes(ref byte value) { - // UInt16 size - if (_tracer is not null) TraceBytes(in value, 16); + // UInt128 size + if (_tracer is not null) TraceBytes(in value, sizeof(HalfWord)); ref byte bytes = ref PushBytesRef(); @@ -230,16 +230,43 @@ public void PushZero() Unsafe.As(ref bytes) = default; } - public void PushUInt32(in int value) + public unsafe void PushUInt32(uint value) { + if (BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + // uint size + if (_tracer is not null) TraceBytes(in Unsafe.As(ref value), sizeof(uint)); + ref byte bytes = ref PushBytesRef(); - // Not full entry, clear first - Unsafe.As(ref bytes) = default; + // First 16+8+4 bytes are zero + Unsafe.As(ref bytes) = default; + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord))) = default; + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord) + sizeof(ulong))) = default; + + // Copy 4 bytes + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord) + sizeof(ulong) + sizeof(uint))) + = value; + } - Span intPlace = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref bytes, WordSize - sizeof(uint)), sizeof(uint)); - BinaryPrimitives.WriteInt32BigEndian(intPlace, value); + public unsafe void PushUInt64(ulong value) + { + if (BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + // uint size + if (_tracer is not null) TraceBytes(in Unsafe.As(ref value), sizeof(ulong)); - _tracer?.ReportStackPush(intPlace); + ref byte bytes = ref PushBytesRef(); + // First 16+8 bytes are zero + Unsafe.As(ref bytes) = default; + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord))) = default; + + // Copy 8 bytes + Unsafe.As(ref Unsafe.Add(ref bytes, sizeof(HalfWord) + sizeof(ulong))) + = value; } /// @@ -382,6 +409,17 @@ public readonly bool PeekUInt256IsZero() return Unsafe.ReadUnaligned(ref bytes).IsZero; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref byte PeekBytesByRef() + { + int head = Head; + if (head-- == 0) + { + return ref Unsafe.NullRef(); + } + return ref Unsafe.Add(ref MemoryMarshal.GetReference(_bytes), head * WordSize); + } + public readonly Span PeekWord256() { int head = Head; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs index 369097c99f0..aaf08aba610 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Runtime.CompilerServices; @@ -7,12 +7,14 @@ namespace Nethermind.Evm; +using Word = Vector256; + internal sealed partial class EvmInstructions { public interface IOpBitwise { virtual static long GasCost => GasCostOf.VeryLow; - abstract static Vector256 Operation(Vector256 a, Vector256 b); + abstract static Word Operation(Word a, Word b); } [SkipLocalsInit] @@ -23,13 +25,15 @@ public static EvmExceptionType InstructionBitwise(VirtualMachine _, ref byte bytesRef = ref stack.PopBytesByRef(); if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 aVec = ReadUnaligned>(ref bytesRef); + Word aVec = ReadUnaligned(ref bytesRef); - bytesRef = ref stack.PopBytesByRef(); + // Peek the top ref to avoid pushing and popping + bytesRef = ref stack.PeekBytesByRef(); if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 bVec = ReadUnaligned>(ref bytesRef); + Word bVec = ReadUnaligned(ref bytesRef); - WriteUnaligned(ref stack.PushBytesRef(), TOpBitwise.Operation(aVec, bVec)); + // Do not need to push as we peeked the last ref, so we can write directly to it + WriteUnaligned(ref bytesRef, TOpBitwise.Operation(aVec, bVec)); return EvmExceptionType.None; // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor @@ -39,22 +43,22 @@ public static EvmExceptionType InstructionBitwise(VirtualMachine _, public struct OpBitwiseAnd : IOpBitwise { - public static Vector256 Operation(Vector256 a, Vector256 b) => Vector256.BitwiseAnd(a, b); + public static Word Operation(Word a, Word b) => Vector256.BitwiseAnd(a, b); } public struct OpBitwiseOr : IOpBitwise { - public static Vector256 Operation(Vector256 a, Vector256 b) => Vector256.BitwiseOr(a, b); + public static Word Operation(Word a, Word b) => Vector256.BitwiseOr(a, b); } public struct OpBitwiseXor : IOpBitwise { - public static Vector256 Operation(Vector256 a, Vector256 b) => Vector256.Xor(a, b); + public static Word Operation(Word a, Word b) => Vector256.Xor(a, b); } public struct OpBitwiseEq : IOpBitwise { - public static Vector256 One = Vector256.Create( + public static Word One = Vector256.Create( (byte) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -62,6 +66,6 @@ public struct OpBitwiseEq : IOpBitwise 0, 0, 0, 0, 0, 0, 0, 1 ); - public static Vector256 Operation(Vector256 a, Vector256 b) => a == b ? One : default; + public static Word Operation(Word a, Word b) => a == b ? One : default; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 66eaf7acf8a..708db7cf7a5 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 736d859eebc..bccf0becf0b 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -160,12 +160,11 @@ public static EvmExceptionType InstructionExtCodeSize(Virt ReadOnlySpan accountCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, followDelegation: false, spec, out _).MachineCode.Span; if (spec.IsEofEnabled && EofValidator.IsEof(accountCode, out _)) { - stack.PushUInt256(2); + stack.PushUInt32(2); } else { - UInt256 result = (UInt256)accountCode.Length; - stack.PushUInt256(in result); + stack.PushUInt32((uint)accountCode.Length); } return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 38bba3315e4..8d0d47b1c4f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index dcb29b03d79..a2c24358221 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -18,14 +18,6 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { - [SkipLocalsInit] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EvmExceptionType InstructionPop(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - gasAvailable -= GasCostOf.Base; - return stack.PopLimbo() ? EvmExceptionType.None : EvmExceptionType.StackUnderflow; - } - [SkipLocalsInit] public static EvmExceptionType InstructionChainId(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -121,72 +113,6 @@ public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref return EvmExceptionType.StackUnderflow; } - [SkipLocalsInit] - public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - where TTracingInstructions : struct, IFlag - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; - EvmState vmState = vm.EvmState; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; - Span bytes = vmState.Memory.LoadSpan(in result); - if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(result, bytes); - - stack.PushBytes(bytes); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor - OutOfGas: - return EvmExceptionType.OutOfGas; - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } - - [SkipLocalsInit] - public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - where TTracingInstructions : struct, IFlag - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; - - Span bytes = stack.PopWord256(); - EvmState vmState = vm.EvmState; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; - vmState.Memory.SaveWord(in result, bytes); - if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange((long)result, bytes); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor - OutOfGas: - return EvmExceptionType.OutOfGas; - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } - - [SkipLocalsInit] - public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - where TTracingInstructions : struct, IFlag - { - gasAvailable -= GasCostOf.VeryLow; - - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; - - byte data = stack.PopByte(); - EvmState vmState = vm.EvmState; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in UInt256.One)) goto OutOfGas; - vmState.Memory.SaveByte(in result, data); - if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(result, data); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor - OutOfGas: - return EvmExceptionType.OutOfGas; - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } - [SkipLocalsInit] public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -247,6 +173,16 @@ public interface IOpEnvUInt256 virtual static long GasCost => GasCostOf.Base; abstract static void Operation(EvmState vmState, out UInt256 result); } + public interface IOpEnvUInt32 + { + virtual static long GasCost => GasCostOf.Base; + abstract static uint Operation(EvmState vmState); + } + public interface IOpEnvUInt64 + { + virtual static long GasCost => GasCostOf.Base; + abstract static ulong Operation(EvmState vmState); + } [SkipLocalsInit] public static EvmExceptionType InstructionEnvBytes(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) @@ -274,6 +210,32 @@ public static EvmExceptionType InstructionEnvUInt256(VirtualMachine vm, return EvmExceptionType.None; } + [SkipLocalsInit] + public static EvmExceptionType InstructionEnvUInt32(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEnv : struct, IOpEnvUInt32 + { + gasAvailable -= TOpEnv.GasCost; + + uint result = TOpEnv.Operation(vm.EvmState); + + stack.PushUInt32(result); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEnvUInt64(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEnv : struct, IOpEnvUInt64 + { + gasAvailable -= TOpEnv.GasCost; + + ulong result = TOpEnv.Operation(vm.EvmState); + + stack.PushUInt64(result); + + return EvmExceptionType.None; + } + public struct OpAddress : IOpEnvBytes { public static void Operation(EvmState vmState, out Span result) @@ -298,16 +260,16 @@ public static void Operation(EvmState vmState, out Span result) => result = vmState.Env.TxExecutionContext.Origin.Bytes; } - public struct OpCallDataSize : IOpEnvUInt256 + public struct OpCallDataSize : IOpEnvUInt32 { - public static void Operation(EvmState vmState, out UInt256 result) - => result = (UInt256)vmState.Env.InputData.Length; + public static uint Operation(EvmState vmState) + => (uint)vmState.Env.InputData.Length; } - public struct OpCodeSize : IOpEnvUInt256 + public struct OpCodeSize : IOpEnvUInt32 { - public static void Operation(EvmState vmState, out UInt256 result) - => result = (UInt256)vmState.Env.CodeInfo.MachineCode.Length; + public static uint Operation(EvmState vmState) + => (uint)vmState.Env.CodeInfo.MachineCode.Length; } public struct OpGasPrice : IOpEnvUInt256 @@ -322,22 +284,22 @@ public static void Operation(EvmState vmState, out Span result) => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasBeneficiary.Bytes; } - public struct OpTimestamp : IOpEnvUInt256 + public struct OpTimestamp : IOpEnvUInt64 { - public static void Operation(EvmState vmState, out UInt256 result) - => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp; + public static ulong Operation(EvmState vmState) + => vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp; } - public struct OpNumber : IOpEnvUInt256 + public struct OpNumber : IOpEnvUInt64 { - public static void Operation(EvmState vmState, out UInt256 result) - => result = (UInt256)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Number; + public static ulong Operation(EvmState vmState) + => (ulong)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Number; } - public struct OpGasLimit : IOpEnvUInt256 + public struct OpGasLimit : IOpEnvUInt64 { - public static void Operation(EvmState vmState, out UInt256 result) - => result = (UInt256)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasLimit; + public static ulong Operation(EvmState vmState) + => (ulong)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasLimit; } public struct OpBaseFee : IOpEnvUInt256 @@ -361,9 +323,9 @@ public static void Operation(EvmState vmState, out UInt256 result) } } - public struct OpMSize : IOpEnvUInt256 + public struct OpMSize : IOpEnvUInt64 { - public static void Operation(EvmState vmState, out UInt256 result) - => result = vmState.Memory.Size; + public static ulong Operation(EvmState vmState) + => vmState.Memory.Size; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 945ab217803..0218778c9e2 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -23,8 +23,7 @@ public static EvmExceptionType InstructionReturnDataSize(VirtualMachine vm, ref { gasAvailable -= GasCostOf.Base; - UInt256 result = (UInt256)vm.ReturnDataBuffer.Length; - stack.PushUInt256(in result); + stack.PushUInt32((uint)vm.ReturnDataBuffer.Length); return EvmExceptionType.None; } @@ -119,7 +118,7 @@ public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmSta if (!UpdateGas(GasCostOf.DataSize, ref gasAvailable)) goto OutOfGas; - stack.PushUInt32(codeInfo.DataSection.Length); + stack.PushUInt32((uint)codeInfo.DataSection.Length); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs index 4ea94375767..f0c955621c5 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs @@ -21,7 +21,7 @@ public static EvmExceptionType InstructionGas(VirtualMachine _, ref EvmStack sta // Ensure gas is positive before pushing to stack if (gasAvailable < 0) goto OutOfGas; - stack.PushUInt256((UInt256)gasAvailable); + stack.PushUInt64((ulong)gasAvailable); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs index 79720089aa6..fe0de67d5dc 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -14,7 +14,7 @@ internal sealed partial class EvmInstructions public static EvmExceptionType InstructionProgramCounter(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; - stack.PushUInt32(programCounter - 1); + stack.PushUInt32((uint)(programCounter - 1)); return EvmExceptionType.None; } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs index d1781462f64..cff44c2c352 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Runtime.CompilerServices; @@ -7,12 +7,14 @@ namespace Nethermind.Evm; +using Word = Vector256; + internal sealed partial class EvmInstructions { public interface IOpMath1Param { virtual static long GasCost => GasCostOf.VeryLow; - abstract static Vector256 Operation(ref byte bytesRef); + abstract static Word Operation(Word value); } [SkipLocalsInit] @@ -21,12 +23,14 @@ public static EvmExceptionType InstructionMath1Param(VirtualMachine _, { gasAvailable -= TOpMath.GasCost; - ref byte bytesRef = ref stack.PopBytesByRef(); + // Peek the top ref to avoid pushing and popping + ref byte bytesRef = ref stack.PeekBytesByRef(); if (IsNullRef(ref bytesRef)) goto StackUnderflow; - Vector256 result = TOpMath.Operation(ref bytesRef); + Word result = TOpMath.Operation(ReadUnaligned(ref bytesRef)); - WriteUnaligned(ref stack.PushBytesRef(), result); + // Do not need to push as we peeked the last ref, so we can write directly to it + WriteUnaligned(ref bytesRef, result); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor @@ -36,11 +40,11 @@ public static EvmExceptionType InstructionMath1Param(VirtualMachine _, public struct OpNot : IOpMath1Param { - public static Vector256 Operation(ref byte bytesRef) => Vector256.OnesComplement(ReadUnaligned>(ref bytesRef)); + public static Word Operation(Word value) => Vector256.OnesComplement(value); } public struct OpIsZero : IOpMath1Param { - public static Vector256 Operation(ref byte bytesRef) => As>(ref bytesRef) == default ? OpBitwiseEq.One : default; + public static Word Operation(Word value) => value == default ? OpBitwiseEq.One : default; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs index eaefb5ce0ba..37a82c640a6 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Runtime.CompilerServices; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs index b565004c922..f70ca24ce1c 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Runtime.CompilerServices; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs index 30de7b87542..f28f370c0bb 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Runtime.CompilerServices; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index 1d984023784..91d0f490048 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -1,18 +1,27 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; using Nethermind.Core; using Nethermind.Core.Crypto; -using Word = System.Runtime.Intrinsics.Vector256; namespace Nethermind.Evm; using Int256; +using Word = Vector256; internal sealed partial class EvmInstructions { + [SkipLocalsInit] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EvmExceptionType InstructionPop(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Base; + return stack.PopLimbo() ? EvmExceptionType.None : EvmExceptionType.StackUnderflow; + } + public interface IOpCount { abstract static int Count { get; } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index da89dbe680d..280187d085b 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -6,13 +6,11 @@ using Nethermind.Core; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; -using Nethermind.Evm.Tracing; using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm; using Int256; - internal sealed partial class EvmInstructions { internal enum StorageAccessType @@ -78,6 +76,72 @@ public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack return EvmExceptionType.StaticCallViolation; } + [SkipLocalsInit] + public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + + Span bytes = stack.PopWord256(); + EvmState vmState = vm.EvmState; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; + vmState.Memory.SaveWord(in result, bytes); + if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange((long)result, bytes); + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + + byte data = stack.PopByte(); + EvmState vmState = vm.EvmState; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in UInt256.One)) goto OutOfGas; + vmState.Memory.SaveByte(in result, data); + if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(result, data); + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TTracingInstructions : struct, IFlag + { + gasAvailable -= GasCostOf.VeryLow; + + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + EvmState vmState = vm.EvmState; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; + Span bytes = vmState.Memory.LoadSpan(in result); + if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(result, bytes); + + stack.PushBytes(bytes); + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + } + [SkipLocalsInit] public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 9a176f2345b..20c607664db 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -66,9 +66,9 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.CALLER] = &InstructionEnvBytes; lookup[(int)Instruction.CALLVALUE] = &InstructionEnvUInt256; lookup[(int)Instruction.CALLDATALOAD] = &InstructionCallDataLoad; - lookup[(int)Instruction.CALLDATASIZE] = &InstructionEnvUInt256; + lookup[(int)Instruction.CALLDATASIZE] = &InstructionEnvUInt32; lookup[(int)Instruction.CALLDATACOPY] = &InstructionCodeCopy; - lookup[(int)Instruction.CODESIZE] = &InstructionEnvUInt256; + lookup[(int)Instruction.CODESIZE] = &InstructionEnvUInt32; lookup[(int)Instruction.CODECOPY] = &InstructionCodeCopy; lookup[(int)Instruction.GASPRICE] = &InstructionEnvUInt256; @@ -89,10 +89,10 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.BLOCKHASH] = &InstructionBlockHash; lookup[(int)Instruction.COINBASE] = &InstructionEnvBytes; - lookup[(int)Instruction.TIMESTAMP] = &InstructionEnvUInt256; - lookup[(int)Instruction.NUMBER] = &InstructionEnvUInt256; + lookup[(int)Instruction.TIMESTAMP] = &InstructionEnvUInt64; + lookup[(int)Instruction.NUMBER] = &InstructionEnvUInt64; lookup[(int)Instruction.PREVRANDAO] = &InstructionPrevRandao; - lookup[(int)Instruction.GASLIMIT] = &InstructionEnvUInt256; + lookup[(int)Instruction.GASLIMIT] = &InstructionEnvUInt64; if (spec.ChainIdOpcodeEnabled) { lookup[(int)Instruction.CHAINID] = &InstructionChainId; @@ -123,7 +123,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.JUMP] = &InstructionJump; lookup[(int)Instruction.JUMPI] = &InstructionJumpIf; lookup[(int)Instruction.PC] = &InstructionProgramCounter; - lookup[(int)Instruction.MSIZE] = &InstructionEnvUInt256; + lookup[(int)Instruction.MSIZE] = &InstructionEnvUInt64; lookup[(int)Instruction.GAS] = &InstructionGas; lookup[(int)Instruction.JUMPDEST] = &InstructionJumpDest; From 05c085126e0a151864bf0686e98d9c64d41ea180 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 01:57:52 +0000 Subject: [PATCH 215/255] Tidy up --- .../Instructions/EvmInstructions.Call.cs | 17 +- .../EvmInstructions.Environment.cs | 357 +++++++++--------- .../Instructions/EvmInstructions.Storage.cs | 8 +- .../Nethermind.Evm/StackAccessTracker.cs | 2 +- src/Nethermind/Nethermind.State/WorldState.cs | 2 +- 5 files changed, 187 insertions(+), 199 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 708db7cf7a5..217bc3ef7c4 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -66,11 +66,6 @@ public static EvmExceptionType InstructionCall(Vi ? codeSource : env.ExecutingAccount; - //if (typeof(TLogger) == typeof(IsTracing)) - //{ - // TraceCallDetails(codeSource, ref callValue, ref transferValue, caller, target); - //} - long gasExtra = 0L; if (!transferValue.IsZero) @@ -124,7 +119,6 @@ public static EvmExceptionType InstructionCall(Vi vm.TxTracer.ReportMemoryChange(dataOffset, memoryTrace is null ? default : memoryTrace.Value.Span); } - //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); if (TTracingInstructions.IsActive) { vm.TxTracer.ReportOperationRemainingGas(gasAvailable); @@ -164,7 +158,7 @@ public static EvmExceptionType InstructionCall(Vi inputData: callData, codeInfo: codeInfo ); - //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Tx call gas {gasLimitUl}"); + if (outputLength == 0) { // TODO: when output length is 0 outputOffset can have any value really @@ -194,15 +188,6 @@ static EvmExceptionType FastCall(VirtualMachine vm, IReleaseSpec spec, in UInt25 return EvmExceptionType.None; } - //[MethodImpl(MethodImplOptions.NoInlining)] - //void TraceCallDetails(Address codeSource, ref UInt256 callValue, ref UInt256 transferValue, Address caller, Address target) - //{ - // _logger.Trace($"caller {caller}"); - // _logger.Trace($"code source {codeSource}"); - // _logger.Trace($"target {target}"); - // _logger.Trace($"value {callValue}"); - // _logger.Trace($"transfer value {transferValue}"); - //} StackUnderflow: return EvmExceptionType.StackUnderflow; OutOfGas: diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index a2c24358221..76401588b22 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -18,6 +18,175 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + public interface IOpEnvBytes + { + virtual static long GasCost => GasCostOf.Base; + abstract static void Operation(EvmState vmState, out Span result); + } + + public interface IOpEnvUInt256 + { + virtual static long GasCost => GasCostOf.Base; + abstract static void Operation(EvmState vmState, out UInt256 result); + } + + public interface IOpEnvUInt32 + { + virtual static long GasCost => GasCostOf.Base; + abstract static uint Operation(EvmState vmState); + } + + public interface IOpEnvUInt64 + { + virtual static long GasCost => GasCostOf.Base; + abstract static ulong Operation(EvmState vmState); + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEnvBytes(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEnv : struct, IOpEnvBytes + { + gasAvailable -= TOpEnv.GasCost; + + TOpEnv.Operation(vm.EvmState, out Span result); + + stack.PushBytes(result); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEnvUInt256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEnv : struct, IOpEnvUInt256 + { + gasAvailable -= TOpEnv.GasCost; + + TOpEnv.Operation(vm.EvmState, out UInt256 result); + + stack.PushUInt256(in result); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEnvUInt32(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEnv : struct, IOpEnvUInt32 + { + gasAvailable -= TOpEnv.GasCost; + + uint result = TOpEnv.Operation(vm.EvmState); + + stack.PushUInt32(result); + + return EvmExceptionType.None; + } + + [SkipLocalsInit] + public static EvmExceptionType InstructionEnvUInt64(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpEnv : struct, IOpEnvUInt64 + { + gasAvailable -= TOpEnv.GasCost; + + ulong result = TOpEnv.Operation(vm.EvmState); + + stack.PushUInt64(result); + + return EvmExceptionType.None; + } + + public struct OpCallDataSize : IOpEnvUInt32 + { + public static uint Operation(EvmState vmState) + => (uint)vmState.Env.InputData.Length; + } + + public struct OpCodeSize : IOpEnvUInt32 + { + public static uint Operation(EvmState vmState) + => (uint)vmState.Env.CodeInfo.MachineCode.Length; + } + + public struct OpTimestamp : IOpEnvUInt64 + { + public static ulong Operation(EvmState vmState) + => vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp; + } + + public struct OpNumber : IOpEnvUInt64 + { + public static ulong Operation(EvmState vmState) + => (ulong)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Number; + } + + public struct OpGasLimit : IOpEnvUInt64 + { + public static ulong Operation(EvmState vmState) + => (ulong)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasLimit; + } + + public struct OpMSize : IOpEnvUInt64 + { + public static ulong Operation(EvmState vmState) + => vmState.Memory.Size; + } + + public struct OpBaseFee : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.BaseFeePerGas; + } + + public struct OpBlobBaseFee : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + { + UInt256? blobBaseFee = vmState.Env.TxExecutionContext.BlockExecutionContext.BlobBaseFee; + if (!blobBaseFee.HasValue) ThrowBadInstruction(); + + result = blobBaseFee.Value; + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowBadInstruction() => throw new BadInstructionException(); + } + } + + public struct OpGasPrice : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = vmState.Env.TxExecutionContext.GasPrice; + } + + public struct OpCallValue : IOpEnvUInt256 + { + public static void Operation(EvmState vmState, out UInt256 result) + => result = vmState.Env.Value; + } + + public struct OpAddress : IOpEnvBytes + { + public static void Operation(EvmState vmState, out Span result) + => result = vmState.Env.ExecutingAccount.Bytes; + } + + public struct OpCaller : IOpEnvBytes + { + public static void Operation(EvmState vmState, out Span result) + => result = vmState.Env.Caller.Bytes; + } + + public struct OpOrigin : IOpEnvBytes + { + public static void Operation(EvmState vmState, out Span result) + => result = vmState.Env.TxExecutionContext.Origin.Bytes; + } + + public struct OpCoinbase : IOpEnvBytes + { + public static void Operation(EvmState vmState, out Span result) + => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasBeneficiary.Bytes; + } + [SkipLocalsInit] public static EvmExceptionType InstructionChainId(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -49,6 +218,17 @@ public static EvmExceptionType InstructionBalance(VirtualMachine vm, ref EvmStac return EvmExceptionType.StackUnderflow; } + [SkipLocalsInit] + public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.SelfBalance; + + UInt256 result = vm.WorldState.GetBalance(vm.EvmState.Env.ExecutingAccount); + stack.PushUInt256(in result); + + return EvmExceptionType.None; + } + [SkipLocalsInit] public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -113,17 +293,6 @@ public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref return EvmExceptionType.StackUnderflow; } - [SkipLocalsInit] - public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - gasAvailable -= GasCostOf.SelfBalance; - - UInt256 result = vm.WorldState.GetBalance(vm.EvmState.Env.ExecutingAccount); - stack.PushUInt256(in result); - - return EvmExceptionType.None; - } - private static bool ChargeAccountAccessGasWithDelegation(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) { IReleaseSpec spec = vm.Spec; @@ -162,170 +331,4 @@ public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine return result; } - - public interface IOpEnvBytes - { - virtual static long GasCost => GasCostOf.Base; - abstract static void Operation(EvmState vmState, out Span result); - } - public interface IOpEnvUInt256 - { - virtual static long GasCost => GasCostOf.Base; - abstract static void Operation(EvmState vmState, out UInt256 result); - } - public interface IOpEnvUInt32 - { - virtual static long GasCost => GasCostOf.Base; - abstract static uint Operation(EvmState vmState); - } - public interface IOpEnvUInt64 - { - virtual static long GasCost => GasCostOf.Base; - abstract static ulong Operation(EvmState vmState); - } - - [SkipLocalsInit] - public static EvmExceptionType InstructionEnvBytes(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - where TOpEnv : struct, IOpEnvBytes - { - gasAvailable -= TOpEnv.GasCost; - - TOpEnv.Operation(vm.EvmState, out Span result); - - stack.PushBytes(result); - - return EvmExceptionType.None; - } - - [SkipLocalsInit] - public static EvmExceptionType InstructionEnvUInt256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - where TOpEnv : struct, IOpEnvUInt256 - { - gasAvailable -= TOpEnv.GasCost; - - TOpEnv.Operation(vm.EvmState, out UInt256 result); - - stack.PushUInt256(in result); - - return EvmExceptionType.None; - } - - [SkipLocalsInit] - public static EvmExceptionType InstructionEnvUInt32(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - where TOpEnv : struct, IOpEnvUInt32 - { - gasAvailable -= TOpEnv.GasCost; - - uint result = TOpEnv.Operation(vm.EvmState); - - stack.PushUInt32(result); - - return EvmExceptionType.None; - } - - [SkipLocalsInit] - public static EvmExceptionType InstructionEnvUInt64(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - where TOpEnv : struct, IOpEnvUInt64 - { - gasAvailable -= TOpEnv.GasCost; - - ulong result = TOpEnv.Operation(vm.EvmState); - - stack.PushUInt64(result); - - return EvmExceptionType.None; - } - - public struct OpAddress : IOpEnvBytes - { - public static void Operation(EvmState vmState, out Span result) - => result = vmState.Env.ExecutingAccount.Bytes; - } - - public struct OpCaller : IOpEnvBytes - { - public static void Operation(EvmState vmState, out Span result) - => result = vmState.Env.Caller.Bytes; - } - - public struct OpCallValue : IOpEnvUInt256 - { - public static void Operation(EvmState vmState, out UInt256 result) - => result = vmState.Env.Value; - } - - public struct OpOrigin : IOpEnvBytes - { - public static void Operation(EvmState vmState, out Span result) - => result = vmState.Env.TxExecutionContext.Origin.Bytes; - } - - public struct OpCallDataSize : IOpEnvUInt32 - { - public static uint Operation(EvmState vmState) - => (uint)vmState.Env.InputData.Length; - } - - public struct OpCodeSize : IOpEnvUInt32 - { - public static uint Operation(EvmState vmState) - => (uint)vmState.Env.CodeInfo.MachineCode.Length; - } - - public struct OpGasPrice : IOpEnvUInt256 - { - public static void Operation(EvmState vmState, out UInt256 result) - => result = vmState.Env.TxExecutionContext.GasPrice; - } - - public struct OpCoinbase : IOpEnvBytes - { - public static void Operation(EvmState vmState, out Span result) - => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasBeneficiary.Bytes; - } - - public struct OpTimestamp : IOpEnvUInt64 - { - public static ulong Operation(EvmState vmState) - => vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp; - } - - public struct OpNumber : IOpEnvUInt64 - { - public static ulong Operation(EvmState vmState) - => (ulong)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Number; - } - - public struct OpGasLimit : IOpEnvUInt64 - { - public static ulong Operation(EvmState vmState) - => (ulong)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasLimit; - } - - public struct OpBaseFee : IOpEnvUInt256 - { - public static void Operation(EvmState vmState, out UInt256 result) - => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.BaseFeePerGas; - } - - public struct OpBlobBaseFee : IOpEnvUInt256 - { - public static void Operation(EvmState vmState, out UInt256 result) - { - UInt256? blobBaseFee = vmState.Env.TxExecutionContext.BlockExecutionContext.BlobBaseFee; - if (!blobBaseFee.HasValue) ThrowBadInstruction(); - - result = blobBaseFee.Value; - - [DoesNotReturn] - [StackTraceHidden] - static void ThrowBadInstruction() => throw new BadInstructionException(); - } - } - - public struct OpMSize : IOpEnvUInt64 - { - public static ulong Operation(EvmState vmState) - => vmState.Memory.Size; - } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index 280187d085b..6a2e99994b2 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -26,7 +26,7 @@ public static EvmExceptionType InstructionTLoad(VirtualMachine vm, ref EvmStack gasAvailable -= GasCostOf.TLoad; if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; - StorageCell storageCell = new(vm.EvmState.Env.ExecutingAccount, result); + StorageCell storageCell = new(vm.EvmState.Env.ExecutingAccount, in result); ReadOnlySpan value = vm.WorldState.GetTransientState(in storageCell); stack.PushBytes(value); @@ -56,7 +56,7 @@ public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack gasAvailable -= GasCostOf.TStore; if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; - StorageCell storageCell = new(vmState.Env.ExecutingAccount, result); + StorageCell storageCell = new(vmState.Env.ExecutingAccount, in result); Span bytes = stack.PopWord256(); vm.WorldState.SetTransientState(in storageCell, !bytes.IsZero() ? bytes.ToArray() : BytesZero32); if (vm.TxTracer.IsTracingStorage) @@ -193,7 +193,7 @@ internal static EvmExceptionType InstructionSStore(Virtual bool newIsZero = bytes.IsZero(); bytes = !newIsZero ? bytes.WithoutLeadingZeros() : BytesZero; - StorageCell storageCell = new(vmState.Env.ExecutingAccount, result); + StorageCell storageCell = new(vmState.Env.ExecutingAccount, in result); if (!ChargeStorageAccessGas( ref gasAvailable, @@ -330,7 +330,7 @@ internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStac if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; Address executingAccount = vm.EvmState.Env.ExecutingAccount; - StorageCell storageCell = new(executingAccount, result); + StorageCell storageCell = new(executingAccount, in result); if (!ChargeStorageAccessGas( ref gasAvailable, vm, diff --git a/src/Nethermind/Nethermind.Evm/StackAccessTracker.cs b/src/Nethermind/Nethermind.Evm/StackAccessTracker.cs index df941be5596..92d3a4a189e 100644 --- a/src/Nethermind/Nethermind.Evm/StackAccessTracker.cs +++ b/src/Nethermind/Nethermind.Evm/StackAccessTracker.cs @@ -58,7 +58,7 @@ public readonly void WarmUp(AccessList? accessList) _trackingState.AccessedAddresses.Add(address); foreach (UInt256 storage in storages) { - _trackingState.AccessedStorageCells.Add(new StorageCell(address, storage)); + _trackingState.AccessedStorageCells.Add(new StorageCell(address, in storage)); } } } diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index fc5ca3eadc5..c95ad044771 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -119,7 +119,7 @@ public void WarmUp(AccessList? accessList) bool exists = _stateProvider.WarmUp(address); foreach (UInt256 storage in storages) { - _persistentStorageProvider.WarmUp(new StorageCell(address, storage), isEmpty: !exists); + _persistentStorageProvider.WarmUp(new StorageCell(address, in storage), isEmpty: !exists); } } } From dbcc2311a586616ed30509b9f1672da29c47278f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 02:10:26 +0000 Subject: [PATCH 216/255] Add comments --- .../EvmInstructions.Environment.cs | 178 +++++++++++++++++- 1 file changed, 173 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index 76401588b22..b1a68c8b8e1 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; @@ -18,43 +18,99 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Defines an environment introspection operation that returns a byte span. + /// Implementations should provide a static gas cost and a static Operation method. + /// public interface IOpEnvBytes { + /// + /// The gas cost for the operation. + /// virtual static long GasCost => GasCostOf.Base; + /// + /// Executes the operation and returns the result as a byte span. + /// + /// The current virtual machine state. + /// The resulting bytes. abstract static void Operation(EvmState vmState, out Span result); } + /// + /// Defines an environment introspection operation that returns a 256-bit unsigned integer. + /// public interface IOpEnvUInt256 { virtual static long GasCost => GasCostOf.Base; + /// + /// Executes the operation and returns the result as a UInt256. + /// + /// The current virtual machine state. + /// The resulting 256-bit unsigned integer. abstract static void Operation(EvmState vmState, out UInt256 result); } + /// + /// Defines an environment introspection operation that returns a 32-bit unsigned integer. + /// public interface IOpEnvUInt32 { virtual static long GasCost => GasCostOf.Base; + /// + /// Executes the operation and returns the result as a UInt32. + /// + /// The current virtual machine state. abstract static uint Operation(EvmState vmState); } + /// + /// Defines an environment introspection operation that returns a 64-bit unsigned integer. + /// public interface IOpEnvUInt64 { virtual static long GasCost => GasCostOf.Base; + /// + /// Executes the operation and returns the result as a UInt64. + /// + /// The current virtual machine state. abstract static ulong Operation(EvmState vmState); } + /// + /// Executes an environment introspection opcode that returns a byte span. + /// Generic parameter TOpEnv defines the concrete operation. + /// + /// The specific operation implementation. + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// An EVM exception type if an error occurs. [SkipLocalsInit] public static EvmExceptionType InstructionEnvBytes(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEnv : struct, IOpEnvBytes { + // Deduct the gas cost as defined by the operation implementation. gasAvailable -= TOpEnv.GasCost; + // Execute the operation and retrieve the result. TOpEnv.Operation(vm.EvmState, out Span result); + // Push the resulting bytes onto the EVM stack. stack.PushBytes(result); return EvmExceptionType.None; } + /// + /// Executes an environment introspection opcode that returns a UInt256 value. + /// + /// The specific operation implementation. + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// An EVM exception type if an error occurs. [SkipLocalsInit] public static EvmExceptionType InstructionEnvUInt256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEnv : struct, IOpEnvUInt256 @@ -68,6 +124,15 @@ public static EvmExceptionType InstructionEnvUInt256(VirtualMachine vm, return EvmExceptionType.None; } + /// + /// Executes an environment introspection opcode that returns a UInt32 value. + /// + /// The specific operation implementation. + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// An EVM exception type if an error occurs. [SkipLocalsInit] public static EvmExceptionType InstructionEnvUInt32(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEnv : struct, IOpEnvUInt32 @@ -81,6 +146,15 @@ public static EvmExceptionType InstructionEnvUInt32(VirtualMachine vm, r return EvmExceptionType.None; } + /// + /// Executes an environment introspection opcode that returns a UInt64 value. + /// + /// The specific operation implementation. + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// An EVM exception type if an error occurs. [SkipLocalsInit] public static EvmExceptionType InstructionEnvUInt64(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpEnv : struct, IOpEnvUInt64 @@ -94,52 +168,78 @@ public static EvmExceptionType InstructionEnvUInt64(VirtualMachine vm, r return EvmExceptionType.None; } + /// + /// Returns the size of the transaction call data. + /// public struct OpCallDataSize : IOpEnvUInt32 { public static uint Operation(EvmState vmState) => (uint)vmState.Env.InputData.Length; } + /// + /// Returns the size of the executing code. + /// public struct OpCodeSize : IOpEnvUInt32 { public static uint Operation(EvmState vmState) => (uint)vmState.Env.CodeInfo.MachineCode.Length; } + /// + /// Returns the timestamp of the current block. + /// public struct OpTimestamp : IOpEnvUInt64 { public static ulong Operation(EvmState vmState) => vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp; } + /// + /// Returns the block number of the current block. + /// public struct OpNumber : IOpEnvUInt64 { public static ulong Operation(EvmState vmState) => (ulong)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.Number; } + /// + /// Returns the gas limit of the current block. + /// public struct OpGasLimit : IOpEnvUInt64 { public static ulong Operation(EvmState vmState) => (ulong)vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasLimit; } + /// + /// Returns the current size of the EVM memory. + /// public struct OpMSize : IOpEnvUInt64 { public static ulong Operation(EvmState vmState) => vmState.Memory.Size; } + /// + /// Returns the base fee per gas for the current block. + /// public struct OpBaseFee : IOpEnvUInt256 { public static void Operation(EvmState vmState, out UInt256 result) => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.BaseFeePerGas; } + /// + /// Returns the blob base fee from the block header. + /// Throws an exception if the blob base fee is not set. + /// public struct OpBlobBaseFee : IOpEnvUInt256 { public static void Operation(EvmState vmState, out UInt256 result) { + // If the blob base fee is missing, this opcode is invalid. UInt256? blobBaseFee = vmState.Env.TxExecutionContext.BlockExecutionContext.BlobBaseFee; if (!blobBaseFee.HasValue) ThrowBadInstruction(); @@ -151,84 +251,120 @@ public static void Operation(EvmState vmState, out UInt256 result) } } + /// + /// Returns the gas price for the transaction. + /// public struct OpGasPrice : IOpEnvUInt256 { public static void Operation(EvmState vmState, out UInt256 result) => result = vmState.Env.TxExecutionContext.GasPrice; } + /// + /// Returns the value transferred with the current call. + /// public struct OpCallValue : IOpEnvUInt256 { public static void Operation(EvmState vmState, out UInt256 result) => result = vmState.Env.Value; } + /// + /// Returns the address of the currently executing account. + /// public struct OpAddress : IOpEnvBytes { public static void Operation(EvmState vmState, out Span result) => result = vmState.Env.ExecutingAccount.Bytes; } + /// + /// Returns the address of the caller of the current execution context. + /// public struct OpCaller : IOpEnvBytes { public static void Operation(EvmState vmState, out Span result) => result = vmState.Env.Caller.Bytes; } + /// + /// Returns the origin address of the transaction. + /// public struct OpOrigin : IOpEnvBytes { public static void Operation(EvmState vmState, out Span result) => result = vmState.Env.TxExecutionContext.Origin.Bytes; } + /// + /// Returns the coinbase (beneficiary) address for the current block. + /// public struct OpCoinbase : IOpEnvBytes { public static void Operation(EvmState vmState, out Span result) => result = vmState.Env.TxExecutionContext.BlockExecutionContext.Header.GasBeneficiary.Bytes; } + /// + /// Pushes the chain identifier onto the stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionChainId(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Base; + // The chain ID is stored as a byte array in the VM stack.PushBytes(vm.ChainId); return EvmExceptionType.None; } + /// + /// Retrieves and pushes the balance of an account. + /// The address is popped from the stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; + // Deduct gas cost for balance operation as per specification. gasAvailable -= spec.GetBalanceCost(); Address address = stack.PopAddress(); if (address is null) goto StackUnderflow; + // Charge gas for account access. If insufficient gas remains, abort. if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; UInt256 result = vm.WorldState.GetBalance(address); stack.PushUInt256(in result); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Pushes the balance of the executing account onto the stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.SelfBalance; + // Get balance for currently executing account. UInt256 result = vm.WorldState.GetBalance(vm.EvmState.Env.ExecutingAccount); stack.PushUInt256(in result); return EvmExceptionType.None; } + /// + /// Retrieves the code hash of an external account. + /// Returns zero if the account does not exist or is considered dead. + /// [SkipLocalsInit] public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -237,26 +373,33 @@ public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref Evm Address address = stack.PopAddress(); if (address is null) goto StackUnderflow; + // Check if enough gas for account access and charge accordingly. if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; IWorldState state = vm.WorldState; + // For dead accounts, the specification requires pushing zero. if (state.IsDeadAccount(address)) { stack.PushZero(); } else { + // Otherwise, push the account's code hash. stack.PushBytes(state.GetCodeHash(address).Bytes); } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: 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. + /// [SkipLocalsInit] public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -275,37 +418,59 @@ public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref 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 + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Charges gas for accessing an account, including potential delegation lookups. + /// This method ensures that both the requested account and its delegated account (if any) are properly charged. + /// + /// Reference to the available gas which will be updated. + /// The virtual machine instance. + /// The target account address. + /// If true, charge even if the account is already warm. + /// True if gas was successfully charged; otherwise false. private static bool ChargeAccountAccessGasWithDelegation(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) { IReleaseSpec spec = vm.Spec; if (!spec.UseHotAndColdStorage) { + // No extra cost if hot/cold storage is not used. return true; } bool notOutOfGas = ChargeAccountAccessGas(ref gasAvailable, vm, address, chargeForWarm); return notOutOfGas && (!vm.EvmState.Env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(vm.WorldState, address, spec, out Address delegated) + // Charge additional gas for the delegated account if it exists. || ChargeAccountAccessGas(ref gasAvailable, vm, delegated, chargeForWarm)); } + /// + /// Charges gas for accessing an account based on its storage state (cold vs. warm). + /// Precompiles are treated as exceptions to the cold/warm gas charge. + /// + /// Reference to the available gas which will be updated. + /// The virtual machine instance. + /// The target account address. + /// If true, applies the warm read gas cost even if the account is warm. + /// True if the gas charge was successful; otherwise false. public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) { bool result = true; @@ -313,11 +478,13 @@ public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine if (spec.UseHotAndColdStorage) { EvmState vmState = vm.EvmState; - if (vm.TxTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list + if (vm.TxTracer.IsTracingAccess) { + // Ensure that tracing simulates access-list behavior. vmState.AccessTracker.WarmUp(address); } + // If the account is cold (and not a precompile), charge the cold access cost. if (vmState.AccessTracker.IsCold(address) && !address.IsPrecompile(spec)) { result = UpdateGas(GasCostOf.ColdAccountAccess, ref gasAvailable); @@ -325,6 +492,7 @@ public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine } else if (chargeForWarm) { + // Otherwise, if warm access should be charged, apply the warm read cost. result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); } } From 8315efd71676c58f58a45f57cfbc7f8a97c6fe2d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 02:17:21 +0000 Subject: [PATCH 217/255] Add comments --- .../Instructions/EvmInstructions.Bitwise.cs | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs index aaf08aba610..7998d853cea 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs @@ -11,53 +11,94 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Represents a bitwise operation on 256-bit vectors. + /// Implementers define a static operation that takes two 256-bit vectors and returns a result vector. + /// public interface IOpBitwise { + /// + /// The gas cost for executing the bitwise operation. + /// virtual static long GasCost => GasCostOf.VeryLow; + /// + /// Executes the bitwise operation. + /// + /// The first operand vector. + /// The second operand vector. + /// The result of the bitwise operation. abstract static Word Operation(Word a, Word b); } + /// + /// Executes a bitwise operation defined by on the top two stack elements. + /// This method reads the operands as 256-bit vectors from unaligned memory and writes the result back directly. + /// + /// The specific bitwise operation to execute. + /// An unused virtual machine instance parameter. + /// The EVM stack from which operands are retrieved and where the result is stored. + /// The remaining gas, reduced by the operation’s cost. + /// The program counter (unused in this operation). + /// An indicating success or a stack underflow error. [SkipLocalsInit] public static EvmExceptionType InstructionBitwise(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpBitwise : struct, IOpBitwise { + // Deduct the operation's gas cost. gasAvailable -= TOpBitwise.GasCost; + // Pop the first operand from the stack by reference to minimize copying. ref byte bytesRef = ref stack.PopBytesByRef(); if (IsNullRef(ref bytesRef)) goto StackUnderflow; + // Read the 256-bit vector from unaligned memory. Word aVec = ReadUnaligned(ref bytesRef); - // Peek the top ref to avoid pushing and popping + // Peek at the top of the stack for the second operand without removing it. bytesRef = ref stack.PeekBytesByRef(); if (IsNullRef(ref bytesRef)) goto StackUnderflow; Word bVec = ReadUnaligned(ref bytesRef); - // Do not need to push as we peeked the last ref, so we can write directly to it + // Write the result directly into the memory of the top stack element. WriteUnaligned(ref bytesRef, TOpBitwise.Operation(aVec, bVec)); return EvmExceptionType.None; - // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Implements the bitwise AND operation on two 256-bit vectors. + /// public struct OpBitwiseAnd : IOpBitwise { public static Word Operation(Word a, Word b) => Vector256.BitwiseAnd(a, b); } + /// + /// Implements the bitwise OR operation on two 256-bit vectors. + /// public struct OpBitwiseOr : IOpBitwise { public static Word Operation(Word a, Word b) => Vector256.BitwiseOr(a, b); } + /// + /// Implements the bitwise XOR operation on two 256-bit vectors. + /// public struct OpBitwiseXor : IOpBitwise { public static Word Operation(Word a, Word b) => Vector256.Xor(a, b); } + /// + /// Performs a bitwise equality check between two 256-bit vectors. + /// If the vectors are equal, returns a vector with the least significant byte set; + /// otherwise, returns a zero vector. + /// public struct OpBitwiseEq : IOpBitwise { + // Precomputed vector used as a marker for equality (only the last byte is set to 1). public static Word One = Vector256.Create( (byte) 0, 0, 0, 0, 0, 0, 0, 0, @@ -66,6 +107,7 @@ public struct OpBitwiseEq : IOpBitwise 0, 0, 0, 0, 0, 0, 0, 1 ); + // Returns a non-zero marker vector if the operands are equal. public static Word Operation(Word a, Word b) => a == b ? One : default; } } From b17f3924865ab3b628203111f8855f23aae8723d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 02:51:12 +0000 Subject: [PATCH 218/255] Comments --- .../Instructions/EvmInstructions.Stack.cs | 351 ++++++++++++++++-- 1 file changed, 310 insertions(+), 41 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index 91d0f490048..813f1bc80cf 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -14,53 +14,103 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Pops a value from the EVM stack. + /// Deducts the base gas cost and returns an exception if the stack is underflowed. + /// + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// if successful; otherwise, . [SkipLocalsInit] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EvmExceptionType InstructionPop(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionPop(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct the minimal gas cost for a POP operation. gasAvailable -= GasCostOf.Base; + // Pop from the stack; if nothing to pop, signal a stack underflow. return stack.PopLimbo() ? EvmExceptionType.None : EvmExceptionType.StackUnderflow; } + /// + /// Interface for series of items based operations. + /// The Count property specifies the expected number of items. + /// public interface IOpCount { + /// + /// The number of items expected. + /// abstract static int Count { get; } + /// + /// This is the default implementation for push operations. + /// Pushes immediate data from the code onto the stack. + /// If insufficient bytes are available, pads the value to the expected length. + /// + /// The expected length of the data. + /// The execution stack. + /// The program counter. + /// The code segment containing the immediate data. [MethodImpl(MethodImplOptions.AggressiveInlining)] virtual static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { + // Use available bytes and pad left if fewer than expected. int usedFromCode = Math.Min(code.Length - programCounter, length); stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); } } + // Some push operations override the default Push method to handle fixed-size optimizations. + + /// + /// 0 item operations. + /// public struct Op0 : IOpCount { public static int Count => 0; } + + /// + /// 1 item operations. + /// public struct Op1 : IOpCount { - const int Size = 1; + const int Size = sizeof(byte); public static int Count => Size; + /// + /// Push operation for a single byte. + /// If exactly one byte is available, it is pushed; otherwise, zero is pushed. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { + // Determine how many bytes can be used from the code. int usedFromCode = Math.Min(code.Length - programCounter, length); if (usedFromCode == Size) { - // Single byte + // Directly push the single byte. ref byte bytes = ref MemoryMarshal.GetReference(code); - stack.PushByte(Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + stack.PushByte(Unsafe.Add(ref bytes, programCounter)); } else { + // Fallback when immediate data is incomplete. stack.PushZero(); } } } + + /// + /// 2 item operations. + /// public struct Op2 : IOpCount { - const int Size = 2; + const int Size = sizeof(ushort); public static int Count => Size; + /// + /// Push operation for two bytes. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { @@ -68,18 +118,29 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read if (usedFromCode == Size) { ref byte bytes = ref MemoryMarshal.GetReference(code); - stack.Push2Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + // Optimized push for exactly two bytes. + stack.Push2Bytes(ref Unsafe.Add(ref bytes, programCounter)); } else { + // Use default padding behavior. stack.PushLeftPaddedBytes(code.Slice(programCounter, usedFromCode), length); } } } + + /// + /// 3 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op3 : IOpCount { public static int Count => 3; } + + /// + /// 4 item operations. + /// public struct Op4 : IOpCount { - const int Size = 4; + const int Size = sizeof(uint); public static int Count => Size; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -89,7 +150,8 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read if (usedFromCode == Size) { ref byte bytes = ref MemoryMarshal.GetReference(code); - stack.Push4Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + // Direct push of a 4-byte value. + stack.Push4Bytes(ref Unsafe.Add(ref bytes, programCounter)); } else { @@ -97,14 +159,36 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read } } } + + /// + /// 5 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op5 : IOpCount { public static int Count => 5; } + + /// + /// 6 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op6 : IOpCount { public static int Count => 6; } + + /// + /// 7 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op7 : IOpCount { public static int Count => 7; } + + /// + /// 8 item operations. + /// public struct Op8 : IOpCount { - const int Size = 8; + const int Size = sizeof(ulong); public static int Count => Size; + /// + /// Push operation for eight bytes. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { @@ -112,7 +196,7 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read if (usedFromCode == Size) { ref byte bytes = ref MemoryMarshal.GetReference(code); - stack.Push8Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + stack.Push8Bytes(ref Unsafe.Add(ref bytes, programCounter)); } else { @@ -120,18 +204,57 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read } } } + + /// + /// 9 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op9 : IOpCount { public static int Count => 9; } + + /// + /// 10 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op10 : IOpCount { public static int Count => 10; } + + /// + /// 11 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op11 : IOpCount { public static int Count => 11; } + + /// + /// 12 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op12 : IOpCount { public static int Count => 12; } + + /// + /// 13 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op13 : IOpCount { public static int Count => 13; } + + /// + /// 14 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op14 : IOpCount { public static int Count => 14; } + + /// + /// 15 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op15 : IOpCount { public static int Count => 15; } + public struct Op16 : IOpCount { const int Size = 16; public static int Count => Size; + /// + /// Push operation for 16 bytes. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { @@ -139,7 +262,7 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read if (usedFromCode == Size) { ref byte bytes = ref MemoryMarshal.GetReference(code); - stack.Push16Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + stack.Push16Bytes(ref Unsafe.Add(ref bytes, programCounter)); } else { @@ -147,23 +270,45 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read } } } + + /// + /// 17 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op17 : IOpCount { public static int Count => 17; } + + /// + /// 18 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op18 : IOpCount { public static int Count => 18; } + + /// + /// 19 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op19 : IOpCount { public static int Count => 19; } + + /// + /// 20 item operations. + /// public struct Op20 : IOpCount { const int Size = 20; public static int Count => Size; + /// + /// Push operation for 20 bytes (commonly used for addresses). + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { int usedFromCode = Math.Min(code.Length - programCounter, length); if (usedFromCode == Size) { - // Address + // Optimized push for address size data. ref byte bytes = ref MemoryMarshal.GetReference(code); - stack.Push20Bytes(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter)); + stack.Push20Bytes(ref Unsafe.Add(ref bytes, programCounter)); } else { @@ -171,28 +316,92 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read } } } + + + /// + /// 21 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op21 : IOpCount { public static int Count => 21; } + + /// + /// 22 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op22 : IOpCount { public static int Count => 22; } + + /// + /// 23 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op23 : IOpCount { public static int Count => 23; } + + /// + /// 24 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op24 : IOpCount { public static int Count => 24; } + + /// + /// 25 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op25 : IOpCount { public static int Count => 25; } + + /// + /// 26 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op26 : IOpCount { public static int Count => 26; } + + /// + /// 27 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op27 : IOpCount { public static int Count => 27; } + + /// + /// 28 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op28 : IOpCount { public static int Count => 28; } + + /// + /// 29 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op29 : IOpCount { public static int Count => 29; } + + /// + /// 30 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op30 : IOpCount { public static int Count => 30; } + + /// + /// 31 item operations. + /// Uses the default implementation for pushing data. + /// public struct Op31 : IOpCount { public static int Count => 31; } + + /// + /// 32 item operations. + /// public struct Op32 : IOpCount { const int Size = 32; public static int Count => Size; + /// + /// Push operation for 32 bytes. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Push(int length, ref EvmStack stack, int programCounter, ReadOnlySpan code) { int usedFromCode = Math.Min(code.Length - programCounter, length); if (usedFromCode == Size) { + // Leverage reinterpretation of bytes as a 256-bit vector. stack.Push32Bytes(in Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter))); } else @@ -202,91 +411,150 @@ public static void Push(int length, ref EvmStack stack, int programCounter, Read } } + /// + /// Handles the PUSH0 opcode which pushes a zero onto the stack. + /// + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// on success. [SkipLocalsInit] - public static EvmExceptionType InstructionDup(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - where TOpCount : IOpCount + public static EvmExceptionType InstructionPush0(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - gasAvailable -= GasCostOf.VeryLow; - if (!stack.Dup(TOpCount.Count)) goto StackUnderflow; - + gasAvailable -= GasCostOf.Base; + stack.PushZero(); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor - StackUnderflow: - return EvmExceptionType.StackUnderflow; } + /// + /// Executes a PUSH instruction. + /// Reads immediate data of a fixed length from the code and pushes it onto the stack. + /// + /// The push operation implementation defining the byte count. + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// Reference to the program counter, which will be advanced. + /// on success. [SkipLocalsInit] - public static EvmExceptionType InstructionSwap(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionPush(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCount : IOpCount { + // Deduct a very low gas cost for the push operation. gasAvailable -= GasCostOf.VeryLow; - if (!stack.Swap(TOpCount.Count + 1)) goto StackUnderflow; - + // Retrieve the code segment containing immediate data. + ReadOnlySpan code = vm.EvmState.Env.CodeInfo.CodeSection.Span; + // Use the push method defined by the specific push operation. + TOpCount.Push(TOpCount.Count, ref stack, programCounter, code); + // Advance the program counter by the number of bytes consumed. + programCounter += TOpCount.Count; return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor - StackUnderflow: - return EvmExceptionType.StackUnderflow; } + /// + /// Executes a DUP operation which duplicates the nth stack element. + /// + /// The duplicate operation implementation that defines which element to duplicate. + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// Reference to the program counter. + /// on success or if insufficient stack elements. [SkipLocalsInit] - public static EvmExceptionType InstructionPush0(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionDup(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + where TOpCount : IOpCount { - gasAvailable -= GasCostOf.Base; - - stack.PushZero(); - + gasAvailable -= GasCostOf.VeryLow; + // Duplicate the nth element from the top; if it fails, signal a stack underflow. + if (!stack.Dup(TOpCount.Count)) goto StackUnderflow; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } + /// + /// Executes a SWAP operation which swaps the top element with the (n+1)th element. + /// + /// The swap operation implementation that defines the swap depth. + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// Reference to the program counter. + /// on success or if insufficient elements. [SkipLocalsInit] - public static EvmExceptionType InstructionPush(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionSwap(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCount : IOpCount { gasAvailable -= GasCostOf.VeryLow; - - ReadOnlySpan code = vm.EvmState.Env.CodeInfo.CodeSection.Span; - - TOpCount.Push(TOpCount.Count, ref stack, programCounter, code); - - programCounter += TOpCount.Count; - + // Swap the top element with the (n+1)th element; ensure adequate stack depth. + if (!stack.Swap(TOpCount.Count + 1)) goto StackUnderflow; return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } + /// + /// Executes a LOG operation which records a log entry with topics and data. + /// Pops data offset and length, then pops a fixed number of topics from the stack. + /// Validates memory expansion and deducts gas accordingly. + /// + /// Specifies the number of log topics (as defined by its Count property). + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// Reference to the program counter. + /// + /// if the log is successfully recorded; otherwise, an appropriate exception type such as + /// , , or . + /// [SkipLocalsInit] public static EvmExceptionType InstructionLog(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpCount : struct, IOpCount { EvmState vmState = vm.EvmState; + // Logging is not permitted in static call contexts. if (vmState.IsStatic) goto StaticCallViolation; + // Pop memory offset and length for the log data. if (!stack.PopUInt256(out UInt256 position) || !stack.PopUInt256(out UInt256 length)) goto StackUnderflow; + + // The number of topics is defined by the generic parameter. long topicsCount = TOpCount.Count; + + // Ensure that the memory expansion for the log data is accounted for. if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, length)) goto OutOfGas; + // Deduct gas for the log entry itself, including per-topic and per-byte data costs. if (!UpdateGas( GasCostOf.Log + topicsCount * GasCostOf.LogTopic + (long)length * GasCostOf.LogData, ref gasAvailable)) goto OutOfGas; + // Load the log data from memory. ReadOnlyMemory data = vmState.Memory.Load(in position, length); + // Prepare the topics array by popping the corresponding number of words from the stack. Hash256[] topics = new Hash256[topicsCount]; for (int i = 0; i < topics.Length; i++) { topics[i] = new Hash256(stack.PopWord256()); } + // Create a new log entry with the executing account, log data, and topics. LogEntry logEntry = new( vmState.Env.ExecutingAccount, data.ToArray(), topics); vmState.AccessTracker.Logs.Add(logEntry); + // Optionally report the log if tracing is enabled. if (vm.TxTracer.IsTracingLogs) { vm.TxTracer.ReportLog(logEntry); } return EvmExceptionType.None; - // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; StaticCallViolation: @@ -295,3 +563,4 @@ public static EvmExceptionType InstructionLog(VirtualMachine vm, ref E return EvmExceptionType.OutOfGas; } } + From e1a46cfb09c2f1eecf972f521b0b8f27b7b88a81 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 03:02:52 +0000 Subject: [PATCH 219/255] Comments --- .../Instructions/EvmInstructions.Shifts.cs | 86 ++++++++++++++++++- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs index f28f370c0bb..aab9c247151 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs @@ -9,76 +9,154 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Interface for shift operations. + /// Implementers define a shift operation that uses a shift amount (provided as a UInt256) + /// to shift a second UInt256 value, returning the shifted result. + /// public interface IOpShift { + /// + /// The gas cost for executing a shift operation. + /// virtual static long GasCost => GasCostOf.VeryLow; + + /// + /// Performs the shift operation. + /// The lower 8 bits of (accessed as a.u0) are used as the shift amount. + /// + /// The shift amount. + /// The value to be shifted. + /// The resulting shifted value. abstract static void Operation(in UInt256 a, in UInt256 b, out UInt256 result); } + /// + /// Executes a shift operation on the EVM stack using the specified . + /// The operation pops the shift amount and the value to shift, unless the shift amount is 256 or more. + /// In that case, the value operand is discarded and zero is pushed as the result. + /// + /// The specific shift operation (e.g. left or right shift). + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// Reference to the program counter. + /// + /// if the operation completes successfully; + /// otherwise, if there are insufficient stack elements. + /// [SkipLocalsInit] public static EvmExceptionType InstructionShift(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpShift : struct, IOpShift { + // Deduct gas cost specific to the shift operation. gasAvailable -= TOpShift.GasCost; + // Pop the shift amount from the stack. if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; + + // If the shift amount is 256 or more, per EVM semantics, discard the second operand and push zero. if (a >= 256) { + // Pop the second operand without using its value. if (!stack.PopLimbo()) goto StackUnderflow; stack.PushZero(); } else { + // Otherwise, pop the value to be shifted. if (!stack.PopUInt256(out UInt256 b)) goto StackUnderflow; + // Perform the shift operation using the specific implementation. TOpShift.Operation(in a, in b, out UInt256 result); stack.PushUInt256(in result); } return EvmExceptionType.None; - // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Executes an arithmetic right shift (SAR) operation. + /// Pops a shift amount and a value from the stack, interprets the value as signed, + /// and performs an arithmetic right shift. + /// + /// The virtual machine instance (unused in the operation logic). + /// The EVM stack used for operands and result storage. + /// Reference to the available gas, reduced by the operation's cost. + /// Reference to the program counter (unused in this operation). + /// + /// if successful; otherwise, + /// if insufficient stack elements are available. + /// [SkipLocalsInit] public static EvmExceptionType InstructionSar(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct the gas cost for the arithmetic shift operation. gasAvailable -= GasCostOf.VeryLow; + // Pop the shift amount and the value to be shifted. if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) goto StackUnderflow; + // If the shift amount is 256 or more, the result depends solely on the sign of the value. if (a >= 256) { + // Convert the unsigned value to a signed integer to determine its sign. if (As(ref b).Sign >= 0) { + // Non-negative value: result is zero. stack.PushZero(); } else { + // Negative value: result is -1 (all bits set). stack.PushSignedInt256(in Int256.MinusOne); } } else { + // For a valid shift amount (<256), perform an arithmetic right shift. As(ref b).RightShift((int)a, out Int256 result); + // Convert the signed result back to unsigned representation. stack.PushUInt256(in As(ref result)); } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Implements a left shift operation. + /// The shift amount is taken from the lower 8 bits of the first operand, and the value from the second operand. + /// public struct OpShl : IOpShift { + /// + /// Performs a left shift: shifts left by the number of bits specified in . + /// + /// The shift amount, where only the lower 8 bits are used. + /// The value to be shifted. + /// The result of the left shift operation. public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) - => result = b << (int)a.u0; + => result = b << (int)a.u0; // Use only the lowest limb (u0) as the shift count. } + /// + /// Implements a right shift operation. + /// The shift amount is taken from the lower 8 bits of the first operand, and the value from the second operand. + /// public struct OpShr : IOpShift { + /// + /// Performs a logical right shift: shifts right by the number of bits specified in . + /// + /// The shift amount, where only the lower 8 bits are used. + /// The value to be shifted. + /// The result of the right shift operation. public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) - => result = b >> (int)a.u0; + => result = b >> (int)a.u0; // Use only the lowest limb (u0) as the shift count. } } From a9342444b50bec8ac5d32936e19be7f46dfe42e0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 03:28:23 +0000 Subject: [PATCH 220/255] Comments --- .../EvmInstructions.Math1Param.cs | 46 +++++++- .../EvmInstructions.Math2Param.cs | 100 ++++++++++++++++-- 2 files changed, 136 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs index cff44c2c352..97f9d1910f3 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs @@ -6,43 +6,81 @@ using static System.Runtime.CompilerServices.Unsafe; namespace Nethermind.Evm; - using Word = Vector256; internal sealed partial class EvmInstructions { + /// + /// Interface for single-parameter mathematical operations on 256‐bit vectors. + /// Implementations provide a specific operation that takes one 256‐bit operand and returns a 256‐bit result. + /// public interface IOpMath1Param { + /// + /// The gas cost for executing the operation. + /// virtual static long GasCost => GasCostOf.VeryLow; + + /// + /// Executes the operation on the provided 256‐bit operand. + /// + /// The input 256‐bit vector. + /// The result of the operation as a 256‐bit vector. abstract static Word Operation(Word value); } + /// + /// Executes a single-parameter mathematical operation on the top element of the EVM stack. + /// The operation is defined by the generic parameter , + /// which implements . + /// + /// A struct implementing for the specific math operation. + /// An unused virtual machine instance. + /// The EVM stack from which the operand is read and where the result is written. + /// Reference to the available gas, reduced by the operation's cost. + /// Reference to the program counter (unused in this operation). + /// + /// if the operation completes successfully; otherwise, + /// if the stack is empty. + /// [SkipLocalsInit] public static EvmExceptionType InstructionMath1Param(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpMath : struct, IOpMath1Param { + // Deduct the gas cost associated with the math operation. gasAvailable -= TOpMath.GasCost; - // Peek the top ref to avoid pushing and popping + // Peek at the top element of the stack without removing it. + // This avoids an unnecessary pop/push sequence. ref byte bytesRef = ref stack.PeekBytesByRef(); if (IsNullRef(ref bytesRef)) goto StackUnderflow; + // Read a 256-bit value from unaligned memory on the stack. Word result = TOpMath.Operation(ReadUnaligned(ref bytesRef)); - // Do not need to push as we peeked the last ref, so we can write directly to it + // Write the computed result directly back to the stack slot. WriteUnaligned(ref bytesRef, result); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Label for error handling when the stack does not have the required element. StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Implements the bitwise NOT operation. + /// Computes the ones' complement of the input 256‐bit vector. + /// public struct OpNot : IOpMath1Param { public static Word Operation(Word value) => Vector256.OnesComplement(value); } + /// + /// Implements the ISZERO operation. + /// Compares the input 256‐bit vector to zero and returns a predefined marker if the value is zero; + /// otherwise, returns a zero vector. + /// public struct OpIsZero : IOpMath1Param { public static Word Operation(Word value) => value == default ? OpBitwiseEq.One : default; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs index 37a82c640a6..1f4214edaa6 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs @@ -10,46 +10,94 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Interface for two-parameter mathematical operations on 256-bit unsigned integers. + /// Implementers define a specific binary math operation (e.g. addition, subtraction). + /// public interface IOpMath2Param { + /// + /// The gas cost for executing this math operation. + /// virtual static long GasCost => GasCostOf.VeryLow; + /// + /// Executes the math operation on two 256-bit operands. + /// + /// The first operand. + /// The second operand. + /// The result of the operation. abstract static void Operation(in UInt256 a, in UInt256 b, out UInt256 result); } + /// + /// Executes a two-parameter mathematical operation. + /// This method pops two UInt256 operands from the stack, applies the operation, + /// and then pushes the result onto the stack. + /// + /// A struct implementing that defines the specific operation. + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// Reference to the program counter. + /// + /// if the operation completes successfully; + /// otherwise, if insufficient stack elements are available. + /// [SkipLocalsInit] - public static EvmExceptionType InstructionMath2Param(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionMath2Param(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TOpMath : struct, IOpMath2Param { + // Deduct the gas cost for the specific math operation. gasAvailable -= TOpMath.GasCost; + // Pop two operands from the stack. If either pop fails, jump to the underflow handler. if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) goto StackUnderflow; + // Execute the math operation defined by TOpMath. TOpMath.Operation(in a, in b, out UInt256 result); + // Push the computed result onto the stack. stack.PushUInt256(in result); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Implements addition of two 256-bit unsigned integers. + /// public struct OpAdd : IOpMath2Param { - public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) => UInt256.Add(in a, in b, out result); + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + => UInt256.Add(in a, in b, out result); } + /// + /// Implements subtraction of two 256-bit unsigned integers. + /// public struct OpSub : IOpMath2Param { - public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) => UInt256.Subtract(in a, in b, out result); + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + => UInt256.Subtract(in a, in b, out result); } + /// + /// Implements multiplication of two 256-bit unsigned integers. + /// Uses a higher gas cost due to the increased computational complexity. + /// public struct OpMul : IOpMath2Param { public static long GasCost => GasCostOf.Low; - public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) => UInt256.Multiply(in a, in b, out result); + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + => UInt256.Multiply(in a, in b, out result); } + /// + /// Implements division of two 256-bit unsigned integers. + /// If the divisor is zero, returns zero per EVM semantics. + /// public struct OpDiv : IOpMath2Param { public static long GasCost => GasCostOf.Low; @@ -57,6 +105,7 @@ public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) { if (b.IsZero) { + // Division by zero yields a result of zero. result = default; } else @@ -66,6 +115,13 @@ public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) } } + /// + /// Implements signed division of two 256-bit integers. + /// Special cases: + /// - Division by zero yields zero. + /// - When dividing the minimum negative value by -1, returns the minimum negative value (to avoid overflow). + /// Otherwise, performs a signed division. + /// public struct OpSDiv : IOpMath2Param { public static long GasCost => GasCostOf.Low; @@ -73,15 +129,19 @@ public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) { if (b.IsZero) { + // Division by zero: result is zero. result = default; } else if (As(ref AsRef(in b)) == Int256.MinusOne && a == P255) { + // Special overflow case: when a equals P255 (a specific constant) and divisor is -1. result = P255; } else { + // Prepare uninitialized result, so doesn't complain when passed by ref in As call. SkipInit(out result); + // Convert operands to signed integers and perform division. Int256.Divide( in As(ref AsRef(in a)), in As(ref AsRef(in b)), @@ -90,12 +150,21 @@ in As(ref AsRef(in b)), } } + /// + /// Implements the modulo operation for 256-bit unsigned integers. + /// public struct OpMod : IOpMath2Param { public static long GasCost => GasCostOf.Low; - public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) => UInt256.Mod(in a, in b, out result); + public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) + => UInt256.Mod(in a, in b, out result); } + /// + /// Implements the signed modulo operation. + /// If the divisor is zero or one, the result is defined as zero. + /// Otherwise, performs the modulo operation on the signed representations. + /// public struct OpSMod : IOpMath2Param { public static long GasCost => GasCostOf.Low; @@ -103,11 +172,14 @@ public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) { if (b.IsZeroOrOne) { + // Modulo with 0 or 1 yields zero. result = default; } else { + // Prepare uninitialized result, so doesn't complain when passed by ref in As call. SkipInit(out result); + // Convert operands to signed integers and perform the modulo operation. As(ref AsRef(in a)) .Mod( in As(ref AsRef(in b)), @@ -116,6 +188,10 @@ in As(ref AsRef(in b)), } } + /// + /// Implements the less-than comparison. + /// Returns 1 if the first operand is less than the second; otherwise, returns 0. + /// public struct OpLt : IOpMath2Param { public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) @@ -124,6 +200,10 @@ public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) } } + /// + /// Implements the greater-than comparison. + /// Returns 1 if the first operand is greater than the second; otherwise, returns 0. + /// public struct OpGt : IOpMath2Param { public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) @@ -132,6 +212,10 @@ public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) } } + /// + /// Implements the signed less-than comparison. + /// Converts unsigned operands to signed representations and returns 1 if the first is less than the second. + /// public struct OpSLt : IOpMath2Param { public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) @@ -143,6 +227,10 @@ public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) } } + /// + /// Implements the signed greater-than comparison. + /// Converts unsigned operands to signed representations and returns 1 if the first is greater than the second. + /// public struct OpSGt : IOpMath2Param { public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) From f4a637203c2f1f01b03c5df5bafac9bcf5acb349 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 03:36:15 +0000 Subject: [PATCH 221/255] Add comments --- .../Instructions/EvmInstructions.Jump.cs | 84 +++++++++++++++++-- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs index fe0de67d5dc..e06fc9b33c0 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs @@ -10,80 +10,148 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Pushes the current program counter (minus one) onto the EVM stack. + /// This is used to obtain the current execution point within the code. + /// + /// The virtual machine instance. + /// The execution stack where the program counter is pushed. + /// Reference to the remaining gas; reduced by the gas cost. + /// The current program counter. + /// + /// on success. + /// [SkipLocalsInit] - public static EvmExceptionType InstructionProgramCounter(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionProgramCounter(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct the base gas cost for reading the program counter. gasAvailable -= GasCostOf.Base; + // The program counter pushed is adjusted by -1 to reflect the correct opcode location. stack.PushUInt32((uint)(programCounter - 1)); return EvmExceptionType.None; } + /// + /// Marks a valid jump destination. + /// This instruction only deducts the jump destination gas cost without modifying the stack. + /// + /// The virtual machine instance. + /// The execution stack. + /// Reference to the remaining gas; reduced by the jump destination cost. + /// The current program counter. + /// + /// on success. + /// [SkipLocalsInit] - public static EvmExceptionType InstructionJumpDest(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionJumpDest(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct the gas cost specific for a jump destination marker. gasAvailable -= GasCostOf.JumpDest; return EvmExceptionType.None; } + /// + /// Executes an unconditional jump. + /// Pops a jump destination from the stack and validates it. + /// If the destination is valid, updates the program counter; otherwise, returns an error. + /// + /// The virtual machine instance. + /// The execution stack from which the jump destination is popped. + /// Reference to the remaining gas; reduced by the gas cost for jumping. + /// Reference to the program counter that may be updated with the jump destination. + /// + /// on success; or + /// on failure. + /// [SkipLocalsInit] public static EvmExceptionType InstructionJump(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct the gas cost for performing a jump. gasAvailable -= GasCostOf.Mid; + // Pop the jump destination from the stack. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + // Validate the jump destination and update the program counter if valid. if (!Jump(result, ref programCounter, in vm.EvmState.Env)) goto InvalidJumpDestination; return EvmExceptionType.None; - // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; InvalidJumpDestination: return EvmExceptionType.InvalidJumpDestination; } + /// + /// Executes a conditional jump. + /// Pops a jump destination and a condition from the stack. If the condition is non-zero, + /// attempts to jump to the specified destination. + /// + /// The virtual machine instance. + /// The execution stack from which the jump destination and condition are popped. + /// Reference to the remaining gas; reduced by the cost for conditional jump. + /// Reference to the program counter that may be updated on a jump. + /// + /// on success; returns + /// or on error. + /// [SkipLocalsInit] [MethodImpl(MethodImplOptions.NoInlining)] public static EvmExceptionType InstructionJumpIf(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct the high gas cost for a conditional jump. gasAvailable -= GasCostOf.High; + // Pop the jump destination. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + // Pop the condition as a byte reference. ref byte condition = ref stack.PopBytesByRef(); if (Unsafe.IsNullRef(in condition)) goto StackUnderflow; + // If the condition is non-zero (i.e., true), attempt to perform the jump. if (Unsafe.As>(ref condition) != default) { if (!Jump(result, ref programCounter, in vm.EvmState.Env)) goto InvalidJumpDestination; } return EvmExceptionType.None; - // Reduce inline code returns, also jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; InvalidJumpDestination: return EvmExceptionType.InvalidJumpDestination; } + /// + /// Validates a jump destination and, if valid, updates the program counter. + /// A valid jump destination must be within the bounds of the code and pass validation rules. + /// + /// The jump destination as a 256-bit unsigned integer. + /// Reference to the program counter that will be updated if the destination is valid. + /// The current execution environment containing code information. + /// + /// true if the destination is valid and the program counter is updated; otherwise, false. + /// [SkipLocalsInit] private static bool Jump(in UInt256 jumpDestination, ref int programCounter, in ExecutionEnvironment env) { bool isJumpDestination = true; + // Check if the jump destination exceeds the maximum allowed integer value. if (jumpDestination > int.MaxValue) { - // https://github.com/NethermindEth/nethermind/issues/140 - // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 0xf435a354924097686ea88dab3aac1dd464e6a3b387c77aeee94145b0fa5a63d2 mainnet isJumpDestination = false; } else { + // Extract the jump destination from the lowest limb of the UInt256. int jumpDestinationInt = (int)jumpDestination.u0; + // Validate that the jump destination corresponds to a valid jump marker in the code. if (!env.CodeInfo.ValidateJump(jumpDestinationInt)) { - // https://github.com/NethermindEth/nethermind/issues/140 - // TODO: add a test, validating inside the condition was not covered by existing tests and fails on 61363 Ropsten isJumpDestination = false; } else { + // Update the program counter to the valid jump destination. programCounter = jumpDestinationInt; } } From d1eacde34773ab567ab5bf5140bc5b8b0577ba11 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 03:51:46 +0000 Subject: [PATCH 222/255] Comments --- .../Instructions/EvmInstructions.CodeCopy.cs | 173 +++++++++++++++--- .../Instructions/EvmInstructions.Extras.cs | 64 ++++++- 2 files changed, 205 insertions(+), 32 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index bccf0becf0b..23c0e8f9ea9 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -12,25 +12,71 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Provides a mechanism to retrieve a code segment for code copy operations. + /// Implementers return a ReadOnlySpan of bytes representing the code to copy. + /// public interface IOpCodeCopy { + /// + /// Gets the code to be copied. + /// + /// The virtual machine instance providing execution context. + /// A read-only span of bytes containing the code. abstract static ReadOnlySpan GetCode(VirtualMachine vm); } + /// + /// Copies a portion of code (or call data) into memory. + /// Pops three parameters from the stack: destination memory offset, source offset, and length. + /// It then deducts gas based on the memory expansion and performs the copy using the provided code source. + /// + /// + /// A struct implementing that defines the code source to copy from. + /// + /// + /// A struct implementing that indicates whether tracing is active. + /// + /// The current virtual machine instance. + /// The EVM stack used for operand retrieval and result storage. + /// Reference to the available gas; reduced by the operation’s cost. + /// Reference to the current program counter (unused in this operation). + /// + /// on success, or an appropriate error code if an error occurs. + /// [SkipLocalsInit] - public static EvmExceptionType InstructionCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCodeCopy( + VirtualMachine vm, + ref EvmStack stack, + ref long gasAvailable, + ref int programCounter) where TOpCodeCopy : struct, IOpCodeCopy where TTracingInstructions : struct, IFlag { - if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + // Pop destination offset, source offset, and copy length. + if (!stack.PopUInt256(out UInt256 a) || + !stack.PopUInt256(out UInt256 b) || + !stack.PopUInt256(out UInt256 result)) + goto StackUnderflow; + + // Deduct gas for the operation plus the cost for memory expansion. + // Gas cost is calculated as a fixed "VeryLow" cost plus a per-32-bytes cost. gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); if (outOfGas) goto OutOfGas; + // Only perform the copy if length (result) is non-zero. if (!result.IsZero) { - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) goto OutOfGas; + // Check and update memory expansion cost. + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) + goto OutOfGas; + + // Obtain the code slice with zero-padding if needed. ZeroPaddedSpan slice = TOpCodeCopy.GetCode(vm).SliceWithZeroPadding(in b, (int)result); + // Save the slice into memory at the destination offset. vm.EvmState.Memory.Save(in a, in slice); + + // If tracing is enabled, report the memory change. if (TTracingInstructions.IsActive) { vm.TxTracer.ReportMemoryChange(a, in slice); @@ -38,49 +84,95 @@ public static EvmExceptionType InstructionCodeCopy + /// Retrieves call data as the source for a code copy. + /// public struct OpCallDataCopy : IOpCodeCopy { public static ReadOnlySpan GetCode(VirtualMachine vm) => vm.EvmState.Env.InputData.Span; } + /// + /// Retrieves the executing code as the source for a code copy. + /// public struct OpCodeCopy : IOpCodeCopy { public static ReadOnlySpan GetCode(VirtualMachine vm) => vm.EvmState.Env.CodeInfo.MachineCode.Span; } + /// + /// Copies external code (from another account) into memory. + /// Pops an address and three parameters (destination offset, source offset, and length) from the stack. + /// Validates account access and memory expansion, then copies the external code into memory. + /// + /// + /// A struct implementing that indicates whether tracing is active. + /// + /// The current virtual machine instance. + /// The EVM stack for operand retrieval and memory copy operations. + /// Reference to the available gas; reduced by both external code access and memory costs. + /// Reference to the program counter (unused in this operation). + /// + /// on success, or an appropriate error code on failure. + /// [SkipLocalsInit] - public static EvmExceptionType InstructionExtCodeCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExtCodeCopy( + VirtualMachine vm, + ref EvmStack stack, + ref long gasAvailable, + ref int programCounter) where TTracingInstructions : struct, IFlag { IReleaseSpec spec = vm.Spec; + // Retrieve the target account address. Address address = stack.PopAddress(); - if (address is null || !stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + // Pop destination offset, source offset, and length from the stack. + if (address is null || + !stack.PopUInt256(out UInt256 a) || + !stack.PopUInt256(out UInt256 b) || + !stack.PopUInt256(out UInt256 result)) + goto StackUnderflow; + // Deduct gas cost: cost for external code access plus memory expansion cost. gasAvailable -= spec.GetExtCodeCost() + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in result, out bool outOfGas); if (outOfGas) goto OutOfGas; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; + // Charge gas for account access (considering hot/cold storage costs). + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) + goto OutOfGas; if (!result.IsZero) { - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) goto OutOfGas; + // Update memory cost if the destination region requires expansion. + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) + goto OutOfGas; + + // Get the external code from the repository. + ReadOnlySpan externalCode = vm.CodeInfoRepository + .GetCachedCodeInfo(vm.WorldState, address, followDelegation: false, spec, out _) + .MachineCode.Span; - ReadOnlySpan externalCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, followDelegation: false, spec, out _).MachineCode.Span; + // 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. vm.EvmState.Memory.Save(in a, in slice); + + // Report memory changes if tracing is enabled. if (TTracingInstructions.IsActive) { vm.TxTracer.ReportMemoryChange(a, in slice); @@ -88,63 +180,87 @@ public static EvmExceptionType InstructionExtCodeCopy(Virt } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Retrieves the size of the external code of an account. + /// Pops an account address from the stack, validates access, and pushes the code size onto the stack. + /// Additionally, applies peephole optimizations for common contract checks. + /// + /// + /// A struct implementing indicating if instruction tracing is active. + /// + /// The virtual machine instance. + /// The EVM stack from which the account address is popped and where the code size is pushed. + /// Reference to the available gas; reduced by external code cost. + /// Reference to the program counter, which may be adjusted during optimization. + /// + /// on success, or an appropriate error code if an error occurs. + /// [SkipLocalsInit] - public static EvmExceptionType InstructionExtCodeSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionExtCodeSize( + VirtualMachine vm, + ref EvmStack stack, + ref long gasAvailable, + ref int programCounter) where TTracingInstructions : struct, IFlag { IReleaseSpec spec = vm.Spec; + // Deduct the gas cost for external code access. gasAvailable -= spec.GetExtCodeCost(); + // Pop the account address from the stack. Address address = stack.PopAddress(); if (address is null) goto StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; + // Charge gas for accessing the account's state. + if (!ChargeAccountAccessGas(ref gasAvailable, vm, address)) + goto OutOfGas; + // Attempt a peephole optimization when tracing is not active and code is available. ReadOnlySpan codeSection = vm.EvmState.Env.CodeInfo.MachineCode.Span; if (!TTracingInstructions.IsActive && programCounter < codeSection.Length) { bool optimizeAccess = false; + // Peek at the next instruction to detect patterns. Instruction nextInstruction = (Instruction)codeSection[programCounter]; - // code.length is zero + // If the next instruction is ISZERO, optimize for a simple contract check. if (nextInstruction == Instruction.ISZERO) { optimizeAccess = true; } - // code.length > 0 || code.length == 0 + // If the next instruction is GT or EQ and the top stack element is zero, + // then this pattern likely corresponds to a contract existence check. else if ((nextInstruction == Instruction.GT || nextInstruction == Instruction.EQ) && - stack.PeekUInt256IsZero()) + stack.PeekUInt256IsZero()) { optimizeAccess = true; + // Remove the zero from the stack since we will have consumed it. if (!stack.PopLimbo()) goto StackUnderflow; } if (optimizeAccess) { - // EXTCODESIZE ISZERO/GT/EQ peephole optimization. - // In solidity 0.8.1+: `return account.code.length > 0;` - // is is a common pattern to check if address is a contract - // however we can just check the address's loaded CodeHash - // to reduce storage access from trying to load the code - + // Peephole optimization for EXTCODESIZE when checking for contract existence. + // This reduces storage access by using the preloaded CodeHash. programCounter++; - // Add gas cost for ISZERO, GT, or EQ + // Deduct very-low gas cost for the next operation (ISZERO, GT, or EQ). gasAvailable -= GasCostOf.VeryLow; - // IsContract + // Determine if the account is a contract by checking the loaded CodeHash. bool isCodeLengthNotZero = vm.WorldState.IsContract(address); + // If the original instruction was GT, invert the check to match the semantics. if (nextInstruction == Instruction.GT) { - // Invert, to IsNotContract isCodeLengthNotZero = !isCodeLengthNotZero; } + // Push 1 if the condition is met (indicating contract presence or absence), else push 0. if (!isCodeLengthNotZero) { stack.PushOne(); @@ -157,17 +273,22 @@ public static EvmExceptionType InstructionExtCodeSize(Virt } } - ReadOnlySpan accountCode = vm.CodeInfoRepository.GetCachedCodeInfo(vm.WorldState, address, followDelegation: false, spec, out _).MachineCode.Span; + // No optimization applied: load the account's code from storage. + ReadOnlySpan accountCode = vm.CodeInfoRepository + .GetCachedCodeInfo(vm.WorldState, address, followDelegation: false, spec, out _) + .MachineCode.Span; + // 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); } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs index f0c955621c5..0b354710657 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs @@ -13,33 +13,64 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Pushes the remaining gas onto the stack. + /// The gas available is decremented by the base cost, and if negative, an OutOfGas error is returned. + /// + /// The virtual machine instance. + /// The execution stack where the gas value will be pushed. + /// Reference to the current available gas, which is modified by this operation. + /// The current program counter. + /// + /// if gas is available, or if the gas becomes negative. + /// [SkipLocalsInit] - public static EvmExceptionType InstructionGas(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionGas(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct the base gas cost for reading gas. gasAvailable -= GasCostOf.Base; - // Ensure gas is positive before pushing to stack + // If gas falls below zero after cost deduction, signal out-of-gas error. if (gasAvailable < 0) goto OutOfGas; + // Push the remaining gas (as unsigned 64-bit) onto the stack. stack.PushUInt64((ulong)gasAvailable); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; } + /// + /// Computes the blob hash from the provided blob versioned hashes. + /// Pops an index from the stack and uses it to select a blob hash from the versioned hashes array. + /// If the index is invalid, pushes zero. + /// + /// The virtual machine instance. + /// The execution stack from which the index is popped and where the blob hash is pushed. + /// Reference to the available gas; reduced by the blob hash cost. + /// The program counter. + /// + /// on success; otherwise, + /// if there are insufficient elements on the stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionBlobHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; + // Deduct the gas cost for blob hash operation. gasAvailable -= GasCostOf.BlobHash; + // Pop the blob index from the stack. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + // Retrieve the array of versioned blob hashes from the execution context. byte[][] versionedHashes = vm.EvmState.Env.TxExecutionContext.BlobVersionedHashes; + // If versioned hashes are available and the index is within range, push the corresponding blob hash. + // Otherwise, push zero. if (versionedHashes is not null && result < versionedHashes.Length) { stack.PushBytes(versionedHashes[result.u0]); @@ -50,30 +81,51 @@ public static EvmExceptionType InstructionBlobHash(VirtualMachine vm, ref EvmSta } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Retrieves a block hash for a given block number. + /// Pops a block number from the stack, validates it, and then pushes the corresponding block hash. + /// If no valid block hash exists, pushes a zero value. + /// Additionally, reports the block hash if block hash tracing is enabled. + /// + /// The virtual machine instance. + /// The execution stack from which the block number is popped and where the block hash is pushed. + /// Reference to the available gas; reduced by the block hash operation cost. + /// The program counter. + /// + /// if the operation completes successfully; + /// otherwise, if there are insufficient stack elements. + /// [SkipLocalsInit] public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct the gas cost for block hash operation. gasAvailable -= GasCostOf.BlockHash; + // Pop the block number from the stack. if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; - long number = a > long.MaxValue ? long.MaxValue : (long)a; + // Convert the block number to a long. Clamp the value to long.MaxValue if it exceeds it. + long number = a > long.MaxValue ? long.MaxValue : (long)a.u0; + + // Retrieve the block hash for the given block number. Hash256? blockHash = vm.BlockHashProvider.GetBlockhash(vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header, number); + // Push the block hash bytes if available; otherwise, push a 32-byte zero value. stack.PushBytes(blockHash is not null ? blockHash.Bytes : BytesZero32); + // If block hash tracing is enabled and a valid block hash was obtained, report it. if (vm.TxTracer.IsTracingBlockHash && blockHash is not null) { vm.TxTracer.ReportBlockHash(blockHash); } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; } From 9bc1317fd7027c524597a88d56601aebce03a65c Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 04:04:35 +0000 Subject: [PATCH 223/255] Add comments --- .../Instructions/EvmInstructions.Call.cs | 177 ++++++++++++++---- 1 file changed, 143 insertions(+), 34 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 217bc3ef7c4..8a503813c3c 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -15,37 +15,116 @@ namespace Nethermind.Evm; internal sealed partial class EvmInstructions { + /// + /// Interface defining the properties for a call-like opcode. + /// Each implementation specifies whether the call is static and what its execution type is. + /// public interface IOpCall { + /// + /// Indicates if the call is static. + /// Static calls cannot modify state. + /// virtual static bool IsStatic => false; + + /// + /// Returns the specific execution type of the call. + /// abstract static ExecutionType ExecutionType { get; } } + /// + /// Represents a normal CALL opcode. + /// + public struct OpCall : IOpCall + { + public static ExecutionType ExecutionType => ExecutionType.CALL; + } + + /// + /// Represents a CALLCODE opcode. + /// + public struct OpCallCode : IOpCall + { + public static ExecutionType ExecutionType => ExecutionType.CALLCODE; + } + + /// + /// Represents a DELEGATECALL opcode. + /// + public struct OpDelegateCall : IOpCall + { + public static ExecutionType ExecutionType => ExecutionType.DELEGATECALL; + } + + /// + /// Represents a STATICCALL opcode. + /// + public struct OpStaticCall : IOpCall + { + public static bool IsStatic => true; + public static ExecutionType ExecutionType => ExecutionType.STATICCALL; + } + + /// + /// Executes a call-like operation. + /// This method handles various call types (CALL, CALLCODE, DELEGATECALL, STATICCALL) by: + /// - Popping call parameters from the stack, + /// - Charging appropriate gas for the call and memory expansion, + /// - Validating call conditions (such as static call restrictions and call depth), + /// - Performing balance transfers, + /// - Creating a new execution frame for the call. + /// + /// + /// The call opcode type (e.g. , ). + /// + /// + /// A type implementing that indicates whether instruction tracing is active. + /// + /// The current virtual machine instance containing execution state. + /// The EVM stack for retrieving call parameters and pushing results. + /// Reference to the available gas, which is deducted according to various call costs. + /// Reference to the current program counter (not modified by this method). + /// + /// An value indicating success or the type of error encountered. + /// [SkipLocalsInit] - public static EvmExceptionType InstructionCall(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCall( + VirtualMachine vm, + ref EvmStack stack, + ref long gasAvailable, + ref int programCounter) where TOpCall : struct, IOpCall where TTracingInstructions : struct, IFlag { + // Increment global call metrics. Metrics.IncrementCalls(); IReleaseSpec spec = vm.Spec; + // Clear previous return data. vm.ReturnData = null; ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; IWorldState state = vm.WorldState; + // Pop the gas limit for the call. if (!stack.PopUInt256(out UInt256 gasLimit)) goto StackUnderflow; + // Pop the code source address from the stack. Address codeSource = stack.PopAddress(); if (codeSource is null) goto StackUnderflow; + // Charge gas for accessing the account's code (including delegation logic if applicable). if (!ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) goto OutOfGas; + // Determine the call value based on the call type. UInt256 callValue; if (typeof(TOpCall) == typeof(OpStaticCall)) { + // Static calls cannot transfer value. callValue = UInt256.Zero; } else if (typeof(TOpCall) == typeof(OpDelegateCall)) { + // Delegate calls use the value from the current execution context. callValue = env.Value; } else if (!stack.PopUInt256(out callValue)) @@ -53,26 +132,34 @@ public static EvmExceptionType InstructionCall(Vi goto StackUnderflow; } + // For non-delegate calls, the transfer value is the call value. UInt256 transferValue = typeof(TOpCall) == typeof(OpDelegateCall) ? UInt256.Zero : callValue; + // Pop additional parameters: data offset, data length, output offset, and output length. if (!stack.PopUInt256(out UInt256 dataOffset) || !stack.PopUInt256(out UInt256 dataLength) || !stack.PopUInt256(out UInt256 outputOffset) || - !stack.PopUInt256(out UInt256 outputLength)) goto StackUnderflow; + !stack.PopUInt256(out UInt256 outputLength)) + goto StackUnderflow; - if (vm.EvmState.IsStatic && !transferValue.IsZero && typeof(TOpCall) != typeof(OpCallCode)) return EvmExceptionType.StaticCallViolation; + // Enforce static call restrictions: no value transfer allowed unless it's a CALLCODE. + if (vm.EvmState.IsStatic && !transferValue.IsZero && typeof(TOpCall) != typeof(OpCallCode)) + return EvmExceptionType.StaticCallViolation; + // Determine caller and target based on the call type. Address caller = typeof(TOpCall) == typeof(OpDelegateCall) ? env.Caller : env.ExecutingAccount; - Address target = typeof(TOpCall) == typeof(OpCall) || typeof(TOpCall) == typeof(OpStaticCall) + Address target = (typeof(TOpCall) == typeof(OpCall) || typeof(TOpCall) == typeof(OpStaticCall)) ? codeSource : env.ExecutingAccount; long gasExtra = 0L; + // Add extra gas cost if value is transferred. if (!transferValue.IsZero) { gasExtra += GasCostOf.CallValue; } + // Charge additional gas if the target account is new or considered empty. if (!spec.ClearEmptyAccountWhenTouched && !state.AccountExists(target)) { gasExtra += GasCostOf.NewAccount; @@ -82,39 +169,49 @@ public static EvmExceptionType InstructionCall(Vi gasExtra += GasCostOf.NewAccount; } + // Update gas: call cost, memory expansion for input and output, and extra gas. if (!UpdateGas(spec.GetCallCost(), ref gasAvailable) || !UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataLength) || !UpdateMemoryCost(vm.EvmState, ref gasAvailable, in outputOffset, outputLength) || - !UpdateGas(gasExtra, ref gasAvailable)) goto OutOfGas; + !UpdateGas(gasExtra, ref gasAvailable)) + goto OutOfGas; + // Retrieve code information for the call and schedule background analysis if needed. ICodeInfo codeInfo = vm.CodeInfoRepository.GetCachedCodeInfo(state, codeSource, spec); codeInfo.AnalyseInBackgroundIfRequired(); + // Apply the 63/64 gas rule if enabled. if (spec.Use63Over64Rule) { gasLimit = UInt256.Min((UInt256)(gasAvailable - gasAvailable / 64), gasLimit); } + // If gasLimit exceeds the host's representable range, treat as out-of-gas. if (gasLimit >= long.MaxValue) goto OutOfGas; long gasLimitUl = (long)gasLimit; if (!UpdateGas(gasLimitUl, ref gasAvailable)) goto OutOfGas; + // Add call stipend if value is being transferred. if (!transferValue.IsZero) { - if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportExtraGasPressure(GasCostOf.CallStipend); + if (vm.TxTracer.IsTracingRefunds) + vm.TxTracer.ReportExtraGasPressure(GasCostOf.CallStipend); gasLimitUl += GasCostOf.CallStipend; } + // Check call depth and balance of the caller. if (env.CallDepth >= MaxCallDepth || - !transferValue.IsZero && state.GetBalance(env.ExecutingAccount) < transferValue) + (!transferValue.IsZero && state.GetBalance(env.ExecutingAccount) < transferValue)) { + // If the call cannot proceed, return an empty response and push zero on the stack. vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); + // Optionally report memory changes for refund tracing. if (vm.TxTracer.IsTracingRefunds) { - // very specific for Parity trace, need to find generalization - very peculiar 32 length... + // Specific to Parity tracing: inspect 32 bytes from data offset. ReadOnlyMemory? memoryTrace = vm.EvmState.Memory.Inspect(in dataOffset, 32); vm.TxTracer.ReportMemoryChange(dataOffset, memoryTrace is null ? default : memoryTrace.Value.Span); } @@ -125,6 +222,7 @@ public static EvmExceptionType InstructionCall(Vi vm.TxTracer.ReportOperationError(EvmExceptionType.NotEnoughBalance); } + // Refund the remaining gas to the caller. UpdateGasUp(gasLimitUl, ref gasAvailable); if (TTracingInstructions.IsActive) { @@ -133,19 +231,23 @@ public static EvmExceptionType InstructionCall(Vi return EvmExceptionType.None; } + // Take a snapshot of the state for potential rollback. Snapshot snapshot = state.TakeSnapshot(); + // Subtract the transfer value from the caller's balance. state.SubtractFromBalance(caller, transferValue, spec); + // Fast-path for calls to externally owned accounts (non-contracts) if (codeInfo.IsEmpty && !TTracingInstructions.IsActive && !vm.TxTracer.IsTracingActions) { - // Non contract call, no need to construct call frame can just credit balance and return gas vm.ReturnDataBuffer = default; stack.PushBytes(StatusCode.SuccessBytes.Span); UpdateGasUp(gasLimitUl, ref gasAvailable); return FastCall(vm, spec, in transferValue, target); } + // Load call data from memory. ReadOnlyMemory callData = vm.EvmState.Memory.Load(in dataOffset, dataLength); + // Construct the execution environment for the call. ExecutionEnvironment callEnv = new ( txExecutionContext: in env.TxExecutionContext, @@ -159,12 +261,14 @@ public static EvmExceptionType InstructionCall(Vi codeInfo: codeInfo ); + // Normalize output offset if output length is zero. if (outputLength == 0) { - // TODO: when output length is 0 outputOffset can have any value really - // and the value does not matter and it can cause trouble when beyond long range + // Output offset is inconsequential when output length is 0. outputOffset = 0; } + + // Rent a new call frame for executing the call. vm.ReturnData = EvmState.RentFrame( gasLimitUl, outputOffset.ToLong(), @@ -178,6 +282,8 @@ public static EvmExceptionType InstructionCall(Vi return EvmExceptionType.None; + // Fast-call path for non-contract calls: + // Directly credit the target account and avoid constructing a full call frame. static EvmExceptionType FastCall(VirtualMachine vm, IReleaseSpec spec, in UInt256 transferValue, Address target) { IWorldState state = vm.WorldState; @@ -188,54 +294,57 @@ static EvmExceptionType FastCall(VirtualMachine vm, IReleaseSpec spec, in UInt25 return EvmExceptionType.None; } + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; OutOfGas: return EvmExceptionType.OutOfGas; } - public struct OpCall : IOpCall - { - public static ExecutionType ExecutionType => ExecutionType.CALL; - } - - public struct OpCallCode : IOpCall - { - public static ExecutionType ExecutionType => ExecutionType.CALLCODE; - } - - public struct OpDelegateCall : IOpCall - { - public static ExecutionType ExecutionType => ExecutionType.DELEGATECALL; - } - - public struct OpStaticCall : IOpCall - { - public static bool IsStatic => true; - public static ExecutionType ExecutionType => ExecutionType.STATICCALL; - } - + /// + /// Executes the RETURN opcode. + /// Pops a memory offset and a length from the stack, updates memory cost, and sets the return data. + /// Returns an error if the opcode is executed in an invalid context. + /// + /// The current virtual machine instance. + /// The EVM stack from which the offset and length are popped. + /// Reference to the available gas, adjusted by memory expansion cost. + /// Reference to the program counter (unused in this operation). + /// + /// on success; otherwise, an error such as , + /// , or . + /// [SkipLocalsInit] - public static EvmExceptionType InstructionReturn(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionReturn( + VirtualMachine vm, + ref EvmStack stack, + ref long gasAvailable, + ref int programCounter) { + // RETURN is not allowed during contract creation. if (vm.EvmState.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)) goto StackUnderflow; + // Update the memory cost for the region being returned. if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in position, in length)) { goto OutOfGas; } + // Load the return data from memory and copy it to an array, + // so the return value isn't referencing live memory, + // which is being unwound in return. vm.ReturnData = vm.EvmState.Memory.Load(in position, in length).ToArray(); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: From 8e7c6adc5168c96c820ecd8dbfc6fe08b241cf03 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 04:16:02 +0000 Subject: [PATCH 224/255] Add comments --- .../Instructions/EvmInstructions.Create.cs | 131 +++++++++++++----- 1 file changed, 100 insertions(+), 31 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 8d0d47b1c4f..83e04009f02 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -14,72 +14,136 @@ namespace Nethermind.Evm; +/// +/// Contains implementations for EVM instructions including contract creation (CREATE and CREATE2). +/// internal sealed partial class EvmInstructions { + /// + /// Interface for CREATE opcode types. + /// Implementations must specify the to distinguish between CREATE and CREATE2. + /// public interface IOpCreate { + /// + /// Gets the execution type corresponding to the create operation. + /// abstract static ExecutionType ExecutionType { get; } } + /// + /// Implements the basic contract creation opcode. + /// + public struct OpCreate : IOpCreate + { + /// + /// Gets the execution type for the CREATE opcode. + /// + public static ExecutionType ExecutionType => ExecutionType.CREATE; + } + + /// + /// Implements the CREATE2 opcode, which allows for deterministic contract address generation. + /// + public struct OpCreate2 : IOpCreate + { + /// + /// Gets the execution type for the CREATE2 opcode. + /// + public static ExecutionType ExecutionType => ExecutionType.CREATE2; + } + + /// + /// Implements the CREATE/CREATE2 opcode, handling new contract deployment. + /// This method performs validation, gas and memory cost calculations, state updates, + /// and delegates execution to a new call frame for the contract's initialization code. + /// + /// The type of create operation (either or ). + /// Tracing instructions type used for instrumentation if active. + /// The current virtual machine instance. + /// Reference to the EVM stack. + /// Reference to the gas counter available for execution. + /// Reference to the program counter. + /// An indicating success or the type of exception encountered. [SkipLocalsInit] - public static EvmExceptionType InstructionCreate(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + public static EvmExceptionType InstructionCreate( + VirtualMachine vm, + ref EvmStack stack, + ref long gasAvailable, + ref int programCounter) where TOpCreate : struct, IOpCreate where TTracingInstructions : struct, IFlag { + // Increment metrics counter for contract creation operations. Metrics.IncrementCreates(); + // Obtain the current EVM specification and check if the call is static (static calls cannot create contracts). IReleaseSpec spec = vm.Spec; - if (vm.EvmState.IsStatic) goto StaticCallViolation; + if (vm.EvmState.IsStatic) + goto StaticCallViolation; + // Reset the return data buffer as contract creation does not use previous return data. vm.ReturnData = null; ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; IWorldState state = vm.WorldState; - // TODO: happens in CREATE_empty000CreateInitCode_Transaction but probably has to be handled differently + // Ensure the executing account exists in the world state. If not, create it with a zero balance. if (!state.AccountExists(env.ExecutingAccount)) { state.CreateAccount(env.ExecutingAccount, UInt256.Zero); } + // Pop parameters off the stack: value to transfer, memory position for the initialization code, + // and the length of the initialization code. if (!stack.PopUInt256(out UInt256 value) || !stack.PopUInt256(out UInt256 memoryPositionOfInitCode) || !stack.PopUInt256(out UInt256 initCodeLength)) goto StackUnderflow; Span salt = default; + // For CREATE2, an extra salt value is required. Use type check to differentiate. if (typeof(TOpCreate) == typeof(OpCreate2)) { salt = stack.PopWord256(); } - //EIP-3860 + // EIP-3860: Limit the maximum size of the initialization code. if (spec.IsEip3860Enabled) { - if (initCodeLength > spec.MaxInitCodeSize) goto OutOfGas; + if (initCodeLength > spec.MaxInitCodeSize) + goto OutOfGas; } bool outOfGas = false; + // Calculate the gas cost for the creation, including fixed cost and per-word cost for init code. + // Also include an extra cost for CREATE2 if applicable. long gasCost = GasCostOf.Create + (spec.IsEip3860Enabled ? GasCostOf.InitCodeWord * EvmPooledMemory.Div32Ceiling(in initCodeLength, out outOfGas) : 0) + (typeof(TOpCreate) == typeof(OpCreate2) ? GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in initCodeLength, out outOfGas) : 0); - if (outOfGas || !UpdateGas(gasCost, ref gasAvailable)) goto OutOfGas; + // Check gas sufficiency: if outOfGas flag was set during gas division or if gas update fails. + if (outOfGas || !UpdateGas(gasCost, ref gasAvailable)) + goto OutOfGas; - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memoryPositionOfInitCode, in initCodeLength)) goto OutOfGas; + // Update memory gas cost based on the required memory expansion for the init code. + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memoryPositionOfInitCode, in initCodeLength)) + goto OutOfGas; - // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? - if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients + // Verify call depth does not exceed the maximum allowed. If exceeded, return early with empty data. + // This guard ensures we do not create nested contract calls beyond EVM limits. + if (env.CallDepth >= MaxCallDepth) { - // TODO: need a test for this vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); goto None; } + // Load the initialization code from memory based on the specified position and length. ReadOnlyMemory initCode = vm.EvmState.Memory.Load(in memoryPositionOfInitCode, in initCodeLength); + // Check that the executing account has sufficient balance to transfer the specified value. UInt256 balance = state.GetBalance(env.ExecutingAccount); if (value > balance) { @@ -88,6 +152,7 @@ public static EvmExceptionType InstructionCreate= maxNonce) @@ -97,25 +162,31 @@ public static EvmExceptionType InstructionCreate(); @@ -124,31 +195,37 @@ public static EvmExceptionType InstructionCreate(); stack.PushZero(); goto None; } + // If the contract address refers to a dead account, clear its storage before creation. if (state.IsDeadAccount(contractAddress)) { state.ClearStorage(contractAddress); } + // Deduct the transfer value from the executing account's balance. state.SubtractFromBalance(env.ExecutingAccount, value, spec); + // Construct a new execution environment for the contract creation call. + // This environment sets up the call frame for executing the contract's initialization code. ExecutionEnvironment callEnv = new ( txExecutionContext: in env.TxExecutionContext, @@ -161,6 +238,8 @@ public static EvmExceptionType InstructionCreate ExecutionType.CREATE; - } - - public struct OpCreate2 : IOpCreate - { - public static ExecutionType ExecutionType => ExecutionType.CREATE2; - } } From 4a1ff055c7f13e392a9cd69d24d8c2cd39284d3c Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 04:29:57 +0000 Subject: [PATCH 225/255] Add comments --- .../Instructions/EvmInstructions.Storage.cs | 347 +++++++++++++++--- 1 file changed, 288 insertions(+), 59 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index 6a2e99994b2..7a15c7d958f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -11,26 +11,61 @@ namespace Nethermind.Evm; using Int256; +/// +/// Implements various EVM instruction handlers for transient storage, memory, and persistent storage operations. +/// internal sealed partial class EvmInstructions { + /// + /// Enumeration for specifying the type of storage access. + /// internal enum StorageAccessType { + /// + /// Indicates a persistent storage read (SLOAD) operation. + /// SLOAD, + + /// + /// Indicates a persistent storage write (SSTORE) operation. + /// SSTORE } + /// + /// Executes the transient load (TLOAD) instruction. + /// + /// Pops an offset from the stack, uses it to construct a storage cell for the executing account, + /// retrieves the corresponding transient storage value, and pushes it onto the stack. + /// + /// + /// The virtual machine instance executing the instruction. + /// The EVM stack. + /// The available gas, which is reduced by the gas cost of the operation. + /// The program counter. + /// An indicating the result of the operation. [SkipLocalsInit] public static EvmExceptionType InstructionTLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Increment the opcode metric for TLOAD. Metrics.TloadOpcode++; + + // Deduct the fixed gas cost for TLOAD. gasAvailable -= GasCostOf.TLoad; + // Attempt to pop the key (offset) from the stack; if unavailable, signal a stack underflow. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + + // Construct a transient storage cell using the executing account and the provided offset. StorageCell storageCell = new(vm.EvmState.Env.ExecutingAccount, in result); + // Retrieve the value from transient storage. ReadOnlySpan value = vm.WorldState.GetTransientState(in storageCell); + + // Push the retrieved value onto the stack. stack.PushBytes(value); + // If storage tracing is enabled, record the operation (ensuring gas remains non-negative). if (vm.TxTracer.IsTracingStorage) { if (gasAvailable < 0) goto OutOfGas; @@ -38,27 +73,52 @@ public static EvmExceptionType InstructionTLoad(VirtualMachine vm, ref EvmStack } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Executes the transient store (TSTORE) instruction. + /// + /// Pops a key and value from the stack, then updates the transient storage for the executing account. + /// In a static call, state modification is disallowed. + /// + /// + /// The virtual machine instance executing the instruction. + /// The EVM stack. + /// The available gas, reduced by the cost of TSTORE. + /// The program counter. + /// An indicating success or failure. [SkipLocalsInit] public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Increment the opcode metric for TSTORE. Metrics.TstoreOpcode++; + EvmState vmState = vm.EvmState; + // Disallow storage modification during static calls. if (vmState.IsStatic) goto StaticCallViolation; + // Deduct the gas cost for TSTORE. gasAvailable -= GasCostOf.TStore; + // Pop the key (offset) from the stack; if unavailable, signal a stack underflow. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + + // Construct a transient storage cell for the executing account at the specified key. StorageCell storageCell = new(vmState.Env.ExecutingAccount, in result); + + // Pop the 32-byte value from the stack. Span bytes = stack.PopWord256(); + + // Store either the actual value (if non-zero) or a predefined zero constant. vm.WorldState.SetTransientState(in storageCell, !bytes.IsZero() ? bytes.ToArray() : BytesZero32); + + // If storage tracing is enabled, retrieve the current stored value and log the operation. if (vm.TxTracer.IsTracingStorage) { if (gasAvailable < 0) goto OutOfGas; @@ -67,7 +127,7 @@ public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: @@ -76,232 +136,365 @@ public static EvmExceptionType InstructionTStore(VirtualMachine vm, ref EvmStack return EvmExceptionType.StaticCallViolation; } + /// + /// Executes the memory store (MSTORE) instruction. + /// + /// Pops an offset and a 32-byte word from the stack, updates the memory cost if needed, + /// and then writes the word into memory. + /// + /// + /// A flag type indicating whether tracing is active. + /// The virtual machine instance. + /// The EVM stack. + /// The remaining gas, which is decremented by both the base and memory extension costs. + /// The program counter. + /// An result. [SkipLocalsInit] public static EvmExceptionType InstructionMStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag { gasAvailable -= GasCostOf.VeryLow; + // Pop the memory offset; if not available, signal a stack underflow. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + // Retrieve the 32-byte word to be stored. Span bytes = stack.PopWord256(); + EvmState vmState = vm.EvmState; + + // Update the memory cost for a 32-byte store; if insufficient gas, signal out-of-gas. if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; + + // Write the 32-byte word into memory. vmState.Memory.SaveWord(in result, bytes); - if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange((long)result, bytes); + + // Report memory changes if tracing is active. + if (TTracingInstructions.IsActive) + vm.TxTracer.ReportMemoryChange((long)result, bytes); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Executes the memory store byte (MSTORE8) instruction. + /// + /// Pops an offset and a byte from the stack, updates the memory cost accordingly, + /// and then stores the single byte at the specified memory location. + /// + /// + /// A flag type indicating whether tracing is active. + /// The virtual machine instance. + /// The EVM stack. + /// The remaining gas, reduced by the operation cost and any memory extension costs. + /// The program counter. + /// An result. [SkipLocalsInit] public static EvmExceptionType InstructionMStore8(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag { gasAvailable -= GasCostOf.VeryLow; + // Pop the memory offset from the stack; if missing, signal a stack underflow. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + // Pop a single byte from the stack. byte data = stack.PopByte(); + EvmState vmState = vm.EvmState; + + // Update the memory cost for a single-byte extension; if insufficient, signal out-of-gas. if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in UInt256.One)) goto OutOfGas; + + // Write the single byte to memory. vmState.Memory.SaveByte(in result, data); - if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(result, data); + + // Report the memory change if tracing is active. + if (TTracingInstructions.IsActive) + vm.TxTracer.ReportMemoryChange(result, data); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Executes the memory load (MLOAD) instruction. + /// + /// Pops an offset from the stack, updates the memory cost for a 32-byte load, + /// retrieves the corresponding memory word, and pushes it onto the stack. + /// + /// + /// A flag type indicating whether tracing is active. + /// The virtual machine instance. + /// The EVM stack. + /// The remaining gas, adjusted for memory access. + /// The program counter. + /// An result. [SkipLocalsInit] public static EvmExceptionType InstructionMLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag { gasAvailable -= GasCostOf.VeryLow; + // Pop the memory offset; if missing, signal a stack underflow. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + EvmState vmState = vm.EvmState; + + // Update memory cost for a 32-byte load. if (!UpdateMemoryCost(vmState, ref gasAvailable, in result, in BigInt32)) goto OutOfGas; + + // Load the 32-byte word from memory. Span bytes = vmState.Memory.LoadSpan(in result); - if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(result, bytes); + // Report the memory load if tracing is active. + if (TTracingInstructions.IsActive) + vm.TxTracer.ReportMemoryChange(result, bytes); + + // Push the loaded bytes onto the stack. stack.PushBytes(bytes); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Executes the memory copy (MCOPY) instruction. + /// + /// Pops destination offset, source offset, and length from the stack, then copies the specified + /// memory region from the source to the destination after verifying that enough gas is available. + /// + /// + /// A flag type indicating whether tracing is active. + /// The virtual machine instance. + /// The EVM stack. + /// The available gas, reduced by both the base cost and the dynamic cost calculated from the length. + /// The program counter. + /// An result. [SkipLocalsInit] public static EvmExceptionType InstructionMCopy(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag { + // Increment the opcode metric for MCOPY. Metrics.MCopyOpcode++; + // Pop destination, source, and length values; if any are missing, signal a stack underflow. if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) goto StackUnderflow; + // Calculate additional gas cost based on the length (using a division rounding-up method) and deduct the total cost. gasAvailable -= GasCostOf.VeryLow + GasCostOf.VeryLow * EvmPooledMemory.Div32Ceiling(c, out bool outOfGas); if (outOfGas) goto OutOfGas; + EvmState vmState = vm.EvmState; + + // Update memory cost for the destination area (largest offset among source and destination) over the specified length. if (!UpdateMemoryCost(vmState, ref gasAvailable, UInt256.Max(b, a), c)) goto OutOfGas; + // Load the specified memory segment from the source offset. Span bytes = vmState.Memory.LoadSpan(in b, c); - if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(b, bytes); + // Report the memory change at the source if tracing is active. + if (TTracingInstructions.IsActive) + vm.TxTracer.ReportMemoryChange(b, bytes); + + // Write the bytes into memory at the destination offset. vmState.Memory.Save(in a, bytes); - if (TTracingInstructions.IsActive) vm.TxTracer.ReportMemoryChange(a, bytes); + + // Report the memory change at the destination if tracing is active. + if (TTracingInstructions.IsActive) + vm.TxTracer.ReportMemoryChange(a, bytes); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } - + /// + /// Executes the storage store (SSTORE) instruction. + /// + /// Pops a key and a value from the stack, performs necessary gas metering (including refunds), + /// and updates persistent storage for the executing account. This method handles both net metered + /// and legacy gas calculations. + /// + /// + /// A flag type indicating whether detailed tracing is enabled. + /// The virtual machine instance. + /// The EVM stack. + /// The available gas, which is decremented by multiple cost adjustments during storage modification. + /// The program counter. + /// An indicating the outcome. [SkipLocalsInit] internal static EvmExceptionType InstructionSStore(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag { + // Increment the SSTORE opcode metric. Metrics.IncrementSStoreOpcode(); + EvmState vmState = vm.EvmState; + // Disallow storage modifications in static calls. if (vmState.IsStatic) goto StaticCallViolation; + IReleaseSpec spec = vm.Spec; - // fail fast before the first storage read if gas is not enough even for reset - if (!spec.UseNetGasMetering && !UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) goto OutOfGas; + // For legacy metering: ensure there is enough gas for the SSTORE reset cost before reading storage. + if (!spec.UseNetGasMetering && !UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) + goto OutOfGas; + + // In net metering with stipend fix, ensure extra gas pressure is reported and that sufficient gas remains. if (spec.UseNetGasMeteringWithAStipendFix) { if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportExtraGasPressure(GasCostOf.CallStipend - spec.GetNetMeteredSStoreCost() + 1); - if (gasAvailable <= GasCostOf.CallStipend) goto OutOfGas; + if (gasAvailable <= GasCostOf.CallStipend) + goto OutOfGas; } + // Pop the key and then the new value for storage; signal underflow if unavailable. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; ReadOnlySpan bytes = stack.PopWord256(); + + // Determine if the new value is effectively zero and normalize non-zero values by stripping leading zeros. bool newIsZero = bytes.IsZero(); bytes = !newIsZero ? bytes.WithoutLeadingZeros() : BytesZero; + // Construct the storage cell for the executing account. StorageCell storageCell = new(vmState.Env.ExecutingAccount, in result); - if (!ChargeStorageAccessGas( - ref gasAvailable, - vm, - in storageCell, - StorageAccessType.SSTORE, - spec)) goto OutOfGas; + // Charge gas based on whether this is a cold or warm storage access. + if (!ChargeStorageAccessGas(ref gasAvailable, vm, in storageCell, StorageAccessType.SSTORE, spec)) + goto OutOfGas; + // Retrieve the current value from persistent storage. ReadOnlySpan currentValue = vm.WorldState.Get(in storageCell); - // Console.WriteLine($"current: {currentValue.ToHexString()} newValue {newValue.ToHexString()}"); bool currentIsZero = currentValue.IsZero(); + // Determine whether the new value is identical to the current stored value. bool newSameAsCurrent = (newIsZero && currentIsZero) || Bytes.AreEqual(currentValue, bytes); + + // Retrieve the refund value associated with clearing storage. long sClearRefunds = RefundOf.SClear(spec.IsEip3529Enabled); - if (!spec.UseNetGasMetering) // note that for this case we already deducted 5000 + // Handle gas adjustments and refunds based on the metering mode. + if (!spec.UseNetGasMetering) { + // Legacy metering: if storing zero and the value changes, grant a clearing refund. if (newIsZero) { if (!newSameAsCurrent) { vmState.Refund += sClearRefunds; - if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(sClearRefunds); + if (vm.TxTracer.IsTracingRefunds) + vm.TxTracer.ReportRefund(sClearRefunds); } } + // When setting a non-zero value over an existing zero, apply the difference in gas costs. else if (currentIsZero) { - if (!UpdateGas(GasCostOf.SSet - GasCostOf.SReset, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.SSet - GasCostOf.SReset, ref gasAvailable)) + goto OutOfGas; } } - else // net metered + else // Net metered calculations. { if (newSameAsCurrent) { - if (!UpdateGas(spec.GetNetMeteredSStoreCost(), ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(spec.GetNetMeteredSStoreCost(), ref gasAvailable)) + goto OutOfGas; } - else // net metered, C != N + else { + // Retrieve the original storage value to determine if this is a reversal. Span originalValue = vm.WorldState.GetOriginal(in storageCell); bool originalIsZero = originalValue.IsZero(); - bool currentSameAsOriginal = Bytes.AreEqual(originalValue, currentValue); + if (currentSameAsOriginal) { if (currentIsZero) { - if (!UpdateGas(GasCostOf.SSet, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.SSet, ref gasAvailable)) + goto OutOfGas; } - else // net metered, current == original != new, !currentIsZero + else { - if (!UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) + goto OutOfGas; if (newIsZero) { vmState.Refund += sClearRefunds; - if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(sClearRefunds); + if (vm.TxTracer.IsTracingRefunds) + vm.TxTracer.ReportRefund(sClearRefunds); } } } - else // net metered, new != current != original + else { long netMeteredStoreCost = spec.GetNetMeteredSStoreCost(); - if (!UpdateGas(netMeteredStoreCost, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(netMeteredStoreCost, ref gasAvailable)) + goto OutOfGas; - if (!originalIsZero) // net metered, new != current != original != 0 + if (!originalIsZero) { + // Adjust refunds based on a change from or to a zero value. if (currentIsZero) { vmState.Refund -= sClearRefunds; - if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(-sClearRefunds); + if (vm.TxTracer.IsTracingRefunds) + vm.TxTracer.ReportRefund(-sClearRefunds); } if (newIsZero) { vmState.Refund += sClearRefunds; - if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(sClearRefunds); + if (vm.TxTracer.IsTracingRefunds) + vm.TxTracer.ReportRefund(sClearRefunds); } } + // If the new value reverts to the original, grant a reversal refund. bool newSameAsOriginal = Bytes.AreEqual(originalValue, bytes); if (newSameAsOriginal) { - long refundFromReversal; - if (originalIsZero) - { - refundFromReversal = spec.GetSetReversalRefund(); - } - else - { - refundFromReversal = spec.GetClearReversalRefund(); - } + long refundFromReversal = originalIsZero + ? spec.GetSetReversalRefund() + : spec.GetClearReversalRefund(); vmState.Refund += refundFromReversal; - if (vm.TxTracer.IsTracingRefunds) vm.TxTracer.ReportRefund(refundFromReversal); + if (vm.TxTracer.IsTracingRefunds) + vm.TxTracer.ReportRefund(refundFromReversal); } } } } + // Only update storage if the new value differs from the current value. if (!newSameAsCurrent) { vm.WorldState.Set(in storageCell, newIsZero ? BytesZero : bytes.ToArray()); } + // Report storage changes for tracing if enabled. if (TTracingInstructions.IsActive) { ReadOnlySpan valueToStore = newIsZero ? BytesZero.AsSpan() : bytes; - byte[] storageBytes = new byte[32]; // do not stackalloc here + byte[] storageBytes = new byte[32]; // Allocated on the heap to avoid stack allocation. storageCell.Index.ToBigEndian(storageBytes); vm.TxTracer.ReportStorageChange(storageBytes, valueToStore); } @@ -312,7 +505,7 @@ internal static EvmExceptionType InstructionSStore(Virtual } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: @@ -321,41 +514,73 @@ internal static EvmExceptionType InstructionSStore(Virtual return EvmExceptionType.StaticCallViolation; } + /// + /// Executes the storage load (SLOAD) instruction. + /// + /// Pops a key from the stack, retrieves the corresponding persistent storage value for the executing account, + /// and pushes that value onto the stack. + /// + /// + /// The virtual machine instance. + /// The EVM stack. + /// The remaining gas, reduced by the SLOAD cost and any storage access gas adjustments. + /// The program counter (unused in this instruction). + /// An indicating the result of the operation. [SkipLocalsInit] internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; + + // Increment the SLOAD opcode metric. Metrics.IncrementSLoadOpcode(); + + // Deduct the gas cost for performing an SLOAD. gasAvailable -= spec.GetSLoadCost(); + // Pop the key from the stack; if unavailable, signal a stack underflow. if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + + // Construct the storage cell for the executing account. Address executingAccount = vm.EvmState.Env.ExecutingAccount; StorageCell storageCell = new(executingAccount, in result); - if (!ChargeStorageAccessGas( - ref gasAvailable, - vm, - in storageCell, - StorageAccessType.SLOAD, - spec)) + + // Charge additional gas based on whether the storage cell is hot or cold. + if (!ChargeStorageAccessGas(ref gasAvailable, vm, in storageCell, StorageAccessType.SLOAD, spec)) { goto OutOfGas; } + // Retrieve the persistent storage value and push it onto the stack. ReadOnlySpan value = vm.WorldState.Get(in storageCell); stack.PushBytes(value); + + // Log the storage load operation if tracing is enabled. if (vm.TxTracer.IsTracingStorage) { vm.TxTracer.LoadOperationStorage(executingAccount, result, value); } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Charges the appropriate gas cost for accessing a storage cell, taking into account whether the access is cold or warm. + /// + /// For cold storage accesses (or if not previously warmed up), a higher gas cost is applied. For warm accesses during SLOAD, + /// a lower cost is deducted. + /// + /// + /// The remaining gas, passed by reference and reduced by the access cost. + /// The virtual machine instance. + /// The target storage cell being accessed. + /// Indicates whether the access is for a load (SLOAD) or store (SSTORE) operation. + /// The release specification which governs gas metering and storage access rules. + /// true if the gas charge was successfully applied; otherwise, false indicating an out-of-gas condition. internal static bool ChargeStorageAccessGas( ref long gasAvailable, VirtualMachine vm, @@ -365,22 +590,26 @@ internal static bool ChargeStorageAccessGas( { EvmState vmState = vm.EvmState; bool result = true; + + // If the spec requires hot/cold storage tracking, determine if extra gas should be charged. if (spec.UseHotAndColdStorage) { + // When tracing access, ensure the storage cell is marked as warm to simulate inclusion in the access list. ref readonly StackAccessTracker accessTracker = ref vmState.AccessTracker; - if (vm.TxTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list + if (vm.TxTracer.IsTracingAccess) { accessTracker.WarmUp(in storageCell); } + // If the storage cell is still cold, apply the higher cold access cost and mark it as warm. if (accessTracker.IsCold(in storageCell)) { result = UpdateGas(GasCostOf.ColdSLoad, ref gasAvailable); accessTracker.WarmUp(in storageCell); } + // For SLOAD operations on already warmed-up storage, apply a lower warm-read cost. else if (storageAccessType == StorageAccessType.SLOAD) { - // we do not charge for WARM_STORAGE_READ_COST in SSTORE scenario result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); } } From 0011aedb8c7d7b01a1864d4f69234278142cead9 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 04:43:01 +0000 Subject: [PATCH 226/255] Add comments --- .../Instructions/EvmInstructions.Eof.cs | 488 ++++++++++++------ 1 file changed, 340 insertions(+), 148 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 0218778c9e2..cee20ba23fa 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -18,45 +18,118 @@ namespace Nethermind.Evm; internal sealed 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 current virtual machine instance. + /// Reference to the operand stack. + /// Reference to the remaining gas counter. + /// Reference to the current program counter. + /// An indicating the outcome. [SkipLocalsInit] public static EvmExceptionType InstructionReturnDataSize(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Deduct base gas cost for this instruction. gasAvailable -= 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 long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag { - if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) + // 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; - gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in c, out bool outOfGas); + // Deduct the fixed gas cost and the memory cost based on the size (rounded up to 32-byte words). + gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size, out bool outOfGas); if (outOfGas) goto OutOfGas; ReadOnlyMemory returnDataBuffer = vm.ReturnDataBuffer; - if (vm.EvmState.Env.CodeInfo.Version == 0 && (UInt256.AddOverflow(c, b, out UInt256 result) || result > returnDataBuffer.Length)) + // For legacy (non-EOF) code, ensure that the copy does not exceed the available return data. + if (vm.EvmState.Env.CodeInfo.Version == 0 && + (UInt256.AddOverflow(size, sourceOffset, out UInt256 result) || result > returnDataBuffer.Length)) { goto AccessViolation; } - if (!c.IsZero) + // Only perform the copy if size is non-zero. + if (!size.IsZero) { - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, c)) return EvmExceptionType.OutOfGas; - ZeroPaddedSpan slice = returnDataBuffer.Span.SliceWithZeroPadding(b, (int)c); - vm.EvmState.Memory.Save(in a, in slice); + // Update memory cost for expanding memory to accommodate the destination slice. + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in destOffset, size)) + 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); + vm.EvmState.Memory.Save(in destOffset, in slice); + + // Report the memory change if tracing is active. if (TTracingInstructions.IsActive) { - vm.TxTracer.ReportMemoryChange(a, in slice); + vm.TxTracer.ReportMemoryChange(destOffset, in slice); } } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: @@ -65,27 +138,45 @@ public static EvmExceptionType InstructionReturnDataCopy(V 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 long gasAvailable, ref int programCounter) { ICodeInfo codeInfo = vm.EvmState.Env.CodeInfo; + // Ensure the instruction is only valid for non-legacy (EOF) code. if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.DataLoad, ref gasAvailable)) goto OutOfGas; + // Deduct gas required for data loading. + if (!UpdateGas(GasCostOf.DataLoad, ref gasAvailable)) + goto OutOfGas; - stack.PopUInt256(out UInt256 a); - ZeroPaddedSpan zpbytes = codeInfo.DataSection.SliceWithZeroPadding(a, 32); + // 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 zpbytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); stack.PushBytes(zpbytes); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // 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 long gasAvailable, ref int programCounter) { @@ -93,22 +184,29 @@ public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmSt if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.DataLoadN, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.DataLoadN, ref gasAvailable)) + goto OutOfGas; - var offset = codeInfo.CodeSection.Span.Slice(programCounter, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + // 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 zpbytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); stack.PushBytes(zpbytes); + // 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 + // 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 long gasAvailable, ref int programCounter) { @@ -116,18 +214,23 @@ public static EvmExceptionType InstructionDataSize(VirtualMachine vm, ref EvmSta if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.DataSize, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.DataSize, ref gasAvailable)) + goto OutOfGas; stack.PushUInt32((uint)codeInfo.DataSection.Length); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // 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 long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag @@ -136,17 +239,26 @@ public static EvmExceptionType InstructionDataCopy(Virtual if (codeInfo.Version == 0) goto BadInstruction; - if (!stack.PopUInt256(out UInt256 memOffset) || !stack.PopUInt256(out UInt256 offset) || !stack.PopUInt256(out UInt256 size)) goto StackUnderflow; + // 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; - if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size, out bool outOfGas), ref gasAvailable) || outOfGas) + // Calculate memory expansion gas cost and deduct overall gas for data copy. + if (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size, out bool outOfGas), ref gasAvailable) + || outOfGas) goto OutOfGas; if (!size.IsZero) { + // Update memory cost for the destination region. if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in memOffset, size)) goto OutOfGas; + // Retrieve the slice from the data section with zero padding if necessary. ZeroPaddedSpan dataSectionSlice = codeInfo.DataSection.SliceWithZeroPadding(offset, (int)size); vm.EvmState.Memory.Save(in memOffset, dataSectionSlice); + if (TTracingInstructions.IsActive) { vm.TxTracer.ReportMemoryChange(memOffset, dataSectionSlice); @@ -154,7 +266,7 @@ public static EvmExceptionType InstructionDataCopy(Virtual } return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; OutOfGas: @@ -163,6 +275,10 @@ public static EvmExceptionType InstructionDataCopy(Virtual 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 long gasAvailable, ref int programCounter) { @@ -170,19 +286,25 @@ public static EvmExceptionType InstructionRelativeJump(VirtualMachine vm, ref Ev if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.RJump, ref gasAvailable)) + 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 + // 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 long gasAvailable, ref int programCounter) { @@ -190,24 +312,33 @@ public static EvmExceptionType InstructionRelativeJumpIf(VirtualMachine vm, ref if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.RJumpi, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.RJumpi, ref gasAvailable)) + 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 + // 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 long gasAvailable, ref int programCounter) { @@ -215,29 +346,39 @@ public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmSt if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.RJumpv, ref gasAvailable)) + goto OutOfGas; - stack.PopUInt256(out UInt256 a); + // 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 immediates = (ushort)(count * EofValidator.TWO_BYTE_LENGTH + EofValidator.ONE_BYTE_LENGTH); - if (a < count) + if (indexValue < count) { - int case_v = programCounter + EofValidator.ONE_BYTE_LENGTH + (int)a * EofValidator.TWO_BYTE_LENGTH; + // 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 immediates. programCounter += immediates; return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // 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 long gasAvailable, ref int programCounter) { @@ -245,20 +386,26 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.Callf, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.Callf, ref gasAvailable)) + 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.EvmState.ReturnStackHead == Eof1.RETURN_STACK_MAX_HEIGHT) goto InvalidSubroutineEntry; + // Push current state onto the return stack. vm.EvmState.ReturnStack[vm.EvmState.ReturnStackHead++] = new EvmState.ReturnState { Index = vm.SectionIndex, @@ -266,11 +413,12 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev 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 + // Jump forward to be unpredicted by the branch predictor. InvalidSubroutineEntry: return EvmExceptionType.InvalidSubroutineEntry; StackOverflow: @@ -281,6 +429,9 @@ public static EvmExceptionType InstructionCallFunction(VirtualMachine vm, ref Ev 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 long gasAvailable, ref int programCounter) { @@ -288,20 +439,26 @@ public static EvmExceptionType InstructionReturnFunction(VirtualMachine vm, ref if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.Retf, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.Retf, ref gasAvailable)) + goto OutOfGas; + // Pop the return state from the return stack. EvmState.ReturnState stackFrame = vm.EvmState.ReturnStack[--vm.EvmState.ReturnStackHead]; vm.SectionIndex = stackFrame.Index; programCounter = stackFrame.Offset; return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // 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 long gasAvailable, ref int programCounter) { @@ -309,11 +466,14 @@ public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref Ev if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.Jumpf, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.Jumpf, ref gasAvailable)) + 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; @@ -322,7 +482,7 @@ public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref Ev programCounter = codeInfo.CodeSectionOffset(index).Start; return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackOverflow: return EvmExceptionType.StackOverflow; OutOfGas: @@ -331,6 +491,10 @@ public static EvmExceptionType InstructionJumpFunction(VirtualMachine vm, ref Ev 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. + /// [SkipLocalsInit] public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -338,21 +502,28 @@ public static EvmExceptionType InstructionDupN(VirtualMachine vm, ref EvmStack s if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.Dupn, ref gasAvailable)) + goto OutOfGas; + // Read the immediate operand. int imm = codeInfo.CodeSection.Span[programCounter]; + // Duplicate the (imm+1)th stack element. stack.Dup(imm + 1); programCounter += 1; return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // 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. + /// [SkipLocalsInit] public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -360,15 +531,18 @@ public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) + goto OutOfGas; + // Immediate operand determines the swap index. int n = 1 + (int)codeInfo.CodeSection.Span[programCounter]; - if (!stack.Swap(n + 1)) goto StackUnderflow; + if (!stack.Swap(n + 1)) + goto StackUnderflow; programCounter += 1; return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; OutOfGas: @@ -377,6 +551,10 @@ public static EvmExceptionType InstructionSwapN(VirtualMachine vm, ref EvmStack 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. + /// [SkipLocalsInit] public static EvmExceptionType InstructionExchange(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -384,24 +562,35 @@ public static EvmExceptionType InstructionExchange(VirtualMachine vm, ref EvmSta if (codeInfo.Version == 0) goto BadInstruction; - if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.Swapn, ref gasAvailable)) + 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 + // 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 long gasAvailable, ref int programCounter) where TTracingInstructions : struct, IFlag @@ -415,64 +604,67 @@ public static EvmExceptionType InstructionEofCreate(Virtua if (codeInfo.Version == 0) goto BadInstruction; - if (vm.EvmState.IsStatic) goto StaticCallViolation; + if (vm.EvmState.IsStatic) + goto StaticCallViolation; - EofCodeInfo container = env.CodeInfo as EofCodeInfo; + // Cast the current code info to EOF-specific container type. + EofCodeInfo container = codeInfo as EofCodeInfo; ExecutionType currentContext = ExecutionType.EOFCREATE; - // 1 - deduct TX_CREATE_COST gas + // 1. Deduct the creation gas cost. if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) goto OutOfGas; ReadOnlySpan codeSection = codeInfo.CodeSection.Span; - // 2 - read immediate operand initcontainer_index, encoded as 8-bit unsigned value + // 2. Read the immediate operand for the init container index. int initcontainerIndex = codeSection[programCounter++]; - // 3 - pop value, salt, input_offset, input_size from the operand stack - // no stack checks becaue EOF guarantees no stack undeflows - if (!stack.PopUInt256(out UInt256 value) || !stack.PopWord256(out Span salt) || !stack.PopUInt256(out UInt256 dataOffset) || !stack.PopUInt256(out UInt256 dataSize)) + // 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 - perform (and charge for) memory expansion using [input_offset, input_size] - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataSize)) goto OutOfGas; + // 4. Charge for memory expansion for the input data. + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataSize)) + goto OutOfGas; - // 5 - load initcode EOF subcontainer at initcontainer_index in the container from which EOFCREATE is executed - // let initcontainer be that EOF container, and initcontainer_size its length in bytes declared in its parent container header + // 5. Load the init code (EOF subcontainer) from the container using the given index. ReadOnlySpan initContainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initcontainerIndex).Value]; - // Eip3860 + // EIP-3860: Check that the init code size does not exceed the maximum allowed. if (spec.IsEip3860Enabled) { - //if (!UpdateGas(GasCostOf.InitCodeWord * numberOfWordInInitcode, ref gasAvailable)) - // return (EvmExceptionType.OutOfGas, null); - if (initContainer.Length > spec.MaxInitCodeSize) goto OutOfGas; + if (initContainer.Length > spec.MaxInitCodeSize) + goto OutOfGas; } - // 6 - deduct GAS_KECCAK256_WORD * ((initcontainer_size + 31) // 32) gas (hashing charge) + // 6. Deduct gas for keccak256 hashing of the init code. long numberOfWordsInInitCode = EvmPooledMemory.Div32Ceiling((UInt256)initContainer.Length, out bool outOfGas); long hashCost = GasCostOf.Sha3Word * numberOfWordsInInitCode; if (outOfGas || !UpdateGas(hashCost, ref gasAvailable)) goto OutOfGas; IWorldState state = vm.WorldState; - // 7 - check that current call depth is below STACK_DEPTH_LIMIT and that caller balance is enough to transfer value - // in case of failure return 0 on the stack, caller’s nonce is not updated and gas for initcode execution is not consumed. + // 7. Check call depth and caller's balance before proceeding with creation. UInt256 balance = state.GetBalance(env.ExecutingAccount); if (env.CallDepth >= MaxCallDepth || value > balance) { - // TODO: need a test for this + // In case of failure, do not consume additional gas. vm.ReturnDataBuffer = Array.Empty(); stack.PushZero(); return EvmExceptionType.None; } - // 8 - caller’s memory slice [input_offset:input_size] is used as calldata + // 8. Prepare the calldata from the caller’s memory slice. Span calldata = vm.EvmState.Memory.LoadSpan(dataOffset, dataSize); - // 9 - execute the container and deduct gas for execution. The 63/64th rule from EIP-150 applies. + // 9. Determine gas available for the new contract execution, applying the 63/64 rule if enabled. long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; - if (!UpdateGas(callGas, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(callGas, ref gasAvailable)) + goto OutOfGas; - // 10 - increment sender account’s nonce + // 10. Increment the nonce of the sender account. UInt256 accountNonce = state.GetNonce(env.ExecutingAccount); UInt256 maxNonce = ulong.MaxValue; if (accountNonce >= maxNonce) @@ -483,41 +675,43 @@ public static EvmExceptionType InstructionEofCreate(Virtua } state.IncrementNonce(env.ExecutingAccount); - // 11 - calculate new_address as keccak256(0xff || sender || salt || keccak256(initcontainer))[12:] + // 11. Calculate the new contract address. Address contractAddress = ContractAddress.From(env.ExecutingAccount, salt, initContainer); if (spec.UseHotAndColdStorage) { - // EIP-2929 assumes that warm-up cost is included in the costs of CREATE and CREATE2 + // Warm up the target address for subsequent storage accesses. vm.EvmState.AccessTracker.WarmUp(contractAddress); } + if (TTracingInstructions.IsActive) + vm.EndInstructionTrace(gasAvailable); - if (TTracingInstructions.IsActive) vm.EndInstructionTrace(gasAvailable); - // todo: === below is a new call - refactor / move - + // Take a snapshot before modifying state for the new contract. Snapshot snapshot = state.TakeSnapshot(); bool accountExists = state.AccountExists(contractAddress); + // If the account already exists and is non-zero, then the creation fails. if (accountExists && contractAddress.IsNonZeroAccount(spec, vm.CodeInfoRepository, state)) { - /* we get the snapshot before this as there is a possibility with that we will touch an empty account and remove it even if the REVERT operation follows */ - //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); 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. ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initContainer.ToArray(), spec, ValidationStrategy.ExractHeader); + // Set up the execution environment for the new contract. ExecutionEnvironment callEnv = new ( txExecutionContext: in env.TxExecutionContext, @@ -543,7 +737,7 @@ in vm.EvmState.AccessTracker ); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StaticCallViolation: return EvmExceptionType.StaticCallViolation; OutOfGas: @@ -552,43 +746,60 @@ in vm.EvmState.AccessTracker 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 InstructionReturnContract(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // This instruction is only valid in create contexts. if (!vm.EvmState.ExecutionType.IsAnyCreateEof()) goto BadInstruction; - if (!UpdateGas(GasCostOf.ReturnContract, ref gasAvailable)) goto OutOfGas; + if (!UpdateGas(GasCostOf.ReturnContract, ref gasAvailable)) + goto OutOfGas; IReleaseSpec spec = vm.Spec; ICodeInfo codeInfo = vm.EvmState.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.ExractHeader); + // Pop memory offset and size for the return data. stack.PopUInt256(out UInt256 a); stack.PopUInt256(out UInt256 b); - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, b)) goto OutOfGas; + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, b)) + 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. vm.ReturnDataBuffer = vm.EvmState.Memory.Load(a, b); vm.ReturnData = deployCodeInfo; return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // 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 long gasAvailable, ref int programCounter) { @@ -599,41 +810,31 @@ public static EvmExceptionType InstructionReturnDataLoad(VirtualMachine vm, ref gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; + if (!stack.PopUInt256(out UInt256 offset)) + goto StackUnderflow; - ZeroPaddedSpan slice = vm.ReturnDataBuffer.Span.SliceWithZeroPadding(a, 32); + ZeroPaddedSpan slice = vm.ReturnDataBuffer.Span.SliceWithZeroPadding(offset, 32); stack.PushBytes(slice); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. StackUnderflow: return EvmExceptionType.StackUnderflow; BadInstruction: return EvmExceptionType.BadInstruction; } - public interface IOpEofCall - { - virtual static bool IsStatic => false; - abstract static ExecutionType ExecutionType { get; } - } - - public struct OpEofCall : IOpEofCall - { - public static ExecutionType ExecutionType => ExecutionType.EOFCALL; - } - - public struct OpEofDelegateCall : IOpEofCall - { - public static ExecutionType ExecutionType => ExecutionType.EOFDELEGATECALL; - } - - public struct OpEofStaticCall : IOpEofCall - { - public static bool IsStatic => true; - public static ExecutionType ExecutionType => ExecutionType.EOFSTATICCALL; - } - + /// + /// 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 long gasAvailable, ref int programCounter) where TOpEofCall : struct, IOpEofCall @@ -648,16 +849,19 @@ public static EvmExceptionType InstructionEofCall targetBytes) || !stack.PopUInt256(out UInt256 dataOffset) || !stack.PopUInt256(out UInt256 dataLength)) + // 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; @@ -677,47 +881,44 @@ public static EvmExceptionType InstructionEofCall= MaxCallDepth) @@ -726,11 +927,10 @@ public static EvmExceptionType InstructionEofCall(); stack.PushOne(); - //if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace("FAIL - call depth"); + // If tracing is active, record additional details regarding the failure. ITxTracer txTracer = vm.TxTracer; if (TTracingInstructions.IsActive) { - // very specific for Parity trace, need to find generalization - very peculiar 32 length... ReadOnlyMemory memoryTrace = vm.EvmState.Memory.Inspect(in dataOffset, 32); txTracer.ReportMemoryChange(dataOffset, memoryTrace.Span); txTracer.ReportOperationRemainingGas(gasAvailable); @@ -741,39 +941,32 @@ public static EvmExceptionType InstructionEofCall(); stack.PushOne(); return EvmExceptionType.None; } - // 10. Perform the call with the available gas and configuration. - if (!UpdateGas(callGas, ref gasAvailable)) goto OutOfGas; + // 12. Deduct gas for the call and prepare the call data. + if (!UpdateGas(callGas, ref gasAvailable)) + goto OutOfGas; ReadOnlyMemory callData = vm.EvmState.Memory.Load(in dataOffset, dataLength); + // 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 = new ( txExecutionContext: in env.TxExecutionContext, @@ -786,7 +979,6 @@ public static EvmExceptionType InstructionEofCall Date: Tue, 4 Feb 2025 05:00:12 +0000 Subject: [PATCH 227/255] Add comments --- .../Instructions/EvmInstructions.cs | 231 ++++++++++++++++-- 1 file changed, 209 insertions(+), 22 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 20c607664db..2b7cea8fae7 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Numerics; using System.Runtime.Intrinsics; using System.Runtime.CompilerServices; using Nethermind.Core; @@ -18,9 +17,18 @@ namespace Nethermind.Evm; internal unsafe sealed partial class EvmInstructions { + /// + /// Generates the opcode lookup table for the Ethereum Virtual Machine. + /// Each of the 256 entries in the returned array corresponds to an EVM instruction, + /// with unassigned opcodes defaulting to a bad instruction handler. + /// + /// A struct implementing IFlag used for tracing purposes. + /// The release specification containing enabled features and opcode flags. + /// An array of function pointers (opcode handlers) indexed by opcode value. public static OpCode[] GenerateOpCodes(IReleaseSpec spec) where TTracingInstructions : struct, IFlag { + // Allocate lookup table for all possible opcodes. var lookup = new delegate*[256]; for (int i = 0; i < lookup.Length; i++) @@ -28,6 +36,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[i] = &InstructionBadInstruction; } + // Set basic control and arithmetic opcodes. lookup[(int)Instruction.STOP] = &InstructionStop; lookup[(int)Instruction.ADD] = &InstructionMath2Param; lookup[(int)Instruction.MUL] = &InstructionMath2Param; @@ -40,6 +49,8 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.MULMOD] = &InstructionMath3Param; lookup[(int)Instruction.EXP] = &InstructionExp; lookup[(int)Instruction.SIGNEXTEND] = &InstructionSignExtend; + + // Comparison and bitwise opcodes. lookup[(int)Instruction.LT] = &InstructionMath2Param; lookup[(int)Instruction.GT] = &InstructionMath2Param; lookup[(int)Instruction.SLT] = &InstructionMath2Param; @@ -51,6 +62,8 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.XOR] = &InstructionBitwise; lookup[(int)Instruction.NOT] = &InstructionMath1Param; lookup[(int)Instruction.BYTE] = &InstructionByte; + + // Conditional: enable shift opcodes if the spec allows. if (spec.ShiftOpcodesEnabled) { lookup[(int)Instruction.SHL] = &InstructionShift; @@ -58,8 +71,10 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.SAR] = &InstructionSar; } + // Cryptographic hash opcode. lookup[(int)Instruction.KECCAK256] = &InstructionKeccak256; + // Environment opcodes. lookup[(int)Instruction.ADDRESS] = &InstructionEnvBytes; lookup[(int)Instruction.BALANCE] = &InstructionBalance; lookup[(int)Instruction.ORIGIN] = &InstructionEnvBytes; @@ -73,14 +88,16 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.GASPRICE] = &InstructionEnvUInt256; lookup[(int)Instruction.EXTCODESIZE] = &InstructionExtCodeSize; - lookup[(int)Instruction.EXTCODECOPY] = &InstructionExtCodeCopy; + // Return data opcodes (if enabled). if (spec.ReturnDataOpcodesEnabled) { lookup[(int)Instruction.RETURNDATASIZE] = &InstructionReturnDataSize; lookup[(int)Instruction.RETURNDATACOPY] = &InstructionReturnDataCopy; } + + // Extended code hash opcode handling. if (spec.ExtCodeHashOpcodeEnabled) { lookup[(int)Instruction.EXTCODEHASH] = spec.IsEofEnabled ? &InstructionExtCodeHashEof : &InstructionExtCodeHash; @@ -88,6 +105,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.BLOCKHASH] = &InstructionBlockHash; + // More environment opcodes. lookup[(int)Instruction.COINBASE] = &InstructionEnvBytes; lookup[(int)Instruction.TIMESTAMP] = &InstructionEnvUInt64; lookup[(int)Instruction.NUMBER] = &InstructionEnvUInt64; @@ -113,13 +131,18 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) { lookup[(int)Instruction.BLOBBASEFEE] = &InstructionEnvUInt256; } - // Gap: 0x4b to 0x4f + + // Gap: opcodes 0x4b to 0x4f are unassigned. + + // Memory and storage instructions. lookup[(int)Instruction.POP] = &InstructionPop; lookup[(int)Instruction.MLOAD] = &InstructionMLoad; lookup[(int)Instruction.MSTORE] = &InstructionMStore; lookup[(int)Instruction.MSTORE8] = &InstructionMStore8; lookup[(int)Instruction.SLOAD] = &InstructionSLoad; lookup[(int)Instruction.SSTORE] = &InstructionSStore; + + // Jump instructions. lookup[(int)Instruction.JUMP] = &InstructionJump; lookup[(int)Instruction.JUMPI] = &InstructionJumpIf; lookup[(int)Instruction.PC] = &InstructionProgramCounter; @@ -127,6 +150,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.GAS] = &InstructionGas; lookup[(int)Instruction.JUMPDEST] = &InstructionJumpDest; + // Transient storage opcodes. if (spec.TransientStorageEnabled) { lookup[(int)Instruction.TLOAD] = &InstructionTLoad; @@ -137,11 +161,13 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.MCOPY] = &InstructionMCopy; } + // Optional PUSH0 instruction. if (spec.IncludePush0Instruction) { lookup[(int)Instruction.PUSH0] = &InstructionPush0; } + // PUSH opcodes (PUSH1 to PUSH32). lookup[(int)Instruction.PUSH1] = &InstructionPush; lookup[(int)Instruction.PUSH2] = &InstructionPush; lookup[(int)Instruction.PUSH3] = &InstructionPush; @@ -175,6 +201,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.PUSH31] = &InstructionPush; lookup[(int)Instruction.PUSH32] = &InstructionPush; + // DUP opcodes (DUP1 to DUP16). lookup[(int)Instruction.DUP1] = &InstructionDup; lookup[(int)Instruction.DUP2] = &InstructionDup; lookup[(int)Instruction.DUP3] = &InstructionDup; @@ -192,6 +219,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.DUP15] = &InstructionDup; lookup[(int)Instruction.DUP16] = &InstructionDup; + // SWAP opcodes (SWAP1 to SWAP16). lookup[(int)Instruction.SWAP1] = &InstructionSwap; lookup[(int)Instruction.SWAP2] = &InstructionSwap; lookup[(int)Instruction.SWAP3] = &InstructionSwap; @@ -209,12 +237,14 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.SWAP15] = &InstructionSwap; lookup[(int)Instruction.SWAP16] = &InstructionSwap; + // LOG opcodes. lookup[(int)Instruction.LOG0] = &InstructionLog; lookup[(int)Instruction.LOG1] = &InstructionLog; lookup[(int)Instruction.LOG2] = &InstructionLog; lookup[(int)Instruction.LOG3] = &InstructionLog; lookup[(int)Instruction.LOG4] = &InstructionLog; + // Extended opcodes for EO (EoF) mode. if (spec.IsEofEnabled) { lookup[(int)Instruction.DATALOAD] = &InstructionDataLoad; @@ -234,6 +264,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.RETURNCONTRACT] = &InstructionReturnContract; } + // Contract creation and call opcodes. lookup[(int)Instruction.CREATE] = &InstructionCreate; lookup[(int)Instruction.CALL] = &InstructionCall; lookup[(int)Instruction.CALLCODE] = &InstructionCall; @@ -253,6 +284,7 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.STATICCALL] = &InstructionCall; } + // Extended call opcodes in EO mode. if (spec.IsEofEnabled) { lookup[(int)Instruction.EXTCALL] = &InstructionEofCall; @@ -271,15 +303,21 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) lookup[(int)Instruction.REVERT] = &InstructionRevert; } + // Final opcodes. lookup[(int)Instruction.INVALID] = &InstructionInvalid; lookup[(int)Instruction.SELFDESTRUCT] = &InstructionSelfDestruct; return lookup; } + /// + /// 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 long gasAvailable, ref int programCounter) { + // In contract creation contexts, a STOP is not permitted. if (vm.EvmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) { return EvmExceptionType.BadInstruction; @@ -288,61 +326,99 @@ public static EvmExceptionType InstructionStop(VirtualMachine vm, ref EvmStack s return EvmExceptionType.Stop; } + /// + /// Implements the REVERT opcode. + /// Pops a memory offset and length from the stack, updates memory gas cost, loads the return data, + /// and returns a revert exception. + /// [SkipLocalsInit] public static EvmExceptionType InstructionRevert(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Attempt to pop memory offset and length; if either fails, signal a stack underflow. if (!stack.PopUInt256(out UInt256 position) || !stack.PopUInt256(out UInt256 length)) - return EvmExceptionType.StackUnderflow; + { + goto StackUnderflow; + } + // Ensure sufficient gas for any required memory expansion. if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in position, in length)) { - return EvmExceptionType.OutOfGas; + goto OutOfGas; } + // Copy the specified memory region as return data. vm.ReturnData = vm.EvmState.Memory.Load(in position, in length).ToArray(); return EvmExceptionType.Revert; + // Jump forward to be unpredicted by the branch predictor. + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; } + /// + /// Executes the SELFDESTRUCT opcode. + /// This method handles gas adjustments, account balance transfers, + /// and marks the executing account for destruction. + /// [SkipLocalsInit] private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Increment metrics for self-destruct operations. Metrics.IncrementSelfDestructs(); EvmState vmState = vm.EvmState; IReleaseSpec spec = vm.Spec; IWorldState state = vm.WorldState; - if (vmState.IsStatic) return EvmExceptionType.StaticCallViolation; + // SELFDESTRUCT is forbidden during static calls. + if (vmState.IsStatic) + goto StaticCallViolation; + // If Shanghai DDoS protection is active, charge the appropriate gas cost. if (spec.UseShanghaiDDosProtection) { gasAvailable -= GasCostOf.SelfDestructEip150; } + // Pop the inheritor address from the stack; signal underflow if missing. Address inheritor = stack.PopAddress(); - if (inheritor is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vm, inheritor, chargeForWarm: false)) return EvmExceptionType.OutOfGas; + if (inheritor is null) + goto StackUnderflow; + + // Charge gas for account access; if insufficient, signal out-of-gas. + if (!ChargeAccountAccessGas(ref gasAvailable, vm, inheritor, chargeForWarm: false)) + goto OutOfGas; Address executingAccount = vmState.Env.ExecutingAccount; bool createInSameTx = vmState.AccessTracker.CreateList.Contains(executingAccount); + // Mark the executing account for destruction if allowed. if (!spec.SelfdestructOnlyOnSameTransaction || createInSameTx) vmState.AccessTracker.ToBeDestroyed(executingAccount); + // Retrieve the current balance for transfer. UInt256 result = state.GetBalance(executingAccount); - if (vm.TxTracer.IsTracingActions) vm.TxTracer.ReportSelfDestruct(executingAccount, result, inheritor); + if (vm.TxTracer.IsTracingActions) + vm.TxTracer.ReportSelfDestruct(executingAccount, result, inheritor); + + // For certain specs, charge gas if transferring to a dead account. if (spec.ClearEmptyAccountWhenTouched && !result.IsZero && state.IsDeadAccount(inheritor)) { - if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) + goto OutOfGas; } + // If account creation rules apply, ensure gas is charged for new accounts. bool inheritorAccountExists = state.AccountExists(inheritor); if (!spec.ClearEmptyAccountWhenTouched && !inheritorAccountExists && spec.UseShanghaiDDosProtection) { - if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) + goto OutOfGas; } + // Create or update the inheritor account with the transferred balance. if (!inheritorAccountExists) { state.CreateAccount(inheritor, result); @@ -352,18 +428,36 @@ private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref E state.AddToBalance(inheritor, result, spec); } + // Special handling when SELFDESTRUCT is limited to the same transaction. if (spec.SelfdestructOnlyOnSameTransaction && !createInSameTx && inheritor.Equals(executingAccount)) - return EvmExceptionType.Stop; // don't burn eth when contract is not destroyed per EIP clarification + goto Stop; // Avoid burning ETH if contract is not destroyed per EIP clarification + // Subtract the balance from the executing account. state.SubtractFromBalance(executingAccount, result, spec); + + // Jump forward to be unpredicted by the branch predictor. + Stop: return EvmExceptionType.Stop; + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + StaticCallViolation: + return EvmExceptionType.StaticCallViolation; } + /// + /// Implements the PREVRANDAO opcode. + /// Pushes the previous random value (post-merge) or block difficulty (pre-merge) onto the stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionPrevRandao(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Charge the base gas cost for this opcode. gasAvailable -= GasCostOf.Base; BlockHeader header = vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header; + + // Use the random value if post-merge; otherwise, use block difficulty. if (header.IsPostMerge) { stack.PushBytes(header.Random.Bytes); @@ -377,31 +471,49 @@ public static EvmExceptionType InstructionPrevRandao(VirtualMachine vm, ref EvmS return EvmExceptionType.None; } + /// + /// Handles invalid opcodes by deducting a high gas cost and returning a BadInstruction error. + /// public static EvmExceptionType InstructionInvalid(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.High; return EvmExceptionType.BadInstruction; } + /// + /// Default handler for undefined opcodes, always returning a BadInstruction error. + /// public static EvmExceptionType InstructionBadInstruction(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) => EvmExceptionType.BadInstruction; + /// + /// Implements the EXP opcode to perform exponentiation. + /// The operation deducts gas based on the size of the exponent and computes the result. + /// [SkipLocalsInit] public static EvmExceptionType InstructionExp(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { + // Charge the fixed gas cost for exponentiation. gasAvailable -= GasCostOf.Exp; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + // Pop the base value from the stack. + if (!stack.PopUInt256(out UInt256 a)) + goto StackUnderflow; + + // Pop the exponent as a 256-bit word. Span bytes = stack.PopWord256(); + // Determine the effective byte-length of the exponent. int leadingZeros = bytes.LeadingZerosCount(); if (leadingZeros == 32) { + // Exponent is zero, so the result is 1. stack.PushOne(); } else { int expSize = 32 - leadingZeros; + // Deduct gas proportional to the number of 32-byte words needed to represent the exponent. gasAvailable -= vm.Spec.GetExpByteCost() * expSize; if (a.IsZero) @@ -414,22 +526,33 @@ public static EvmExceptionType InstructionExp(VirtualMachine vm, ref EvmStack st } else { + // Perform exponentiation and push the 256-bit result onto the stack. UInt256.Exp(a, new UInt256(bytes, true), out UInt256 result); stack.PushUInt256(in result); } } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } + /// + /// Implements the BYTE opcode. + /// Extracts a byte from a 256-bit word at the position specified by the stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionByte(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + // Pop the byte position and the 256-bit word. + if (!stack.PopUInt256(out UInt256 a)) + goto StackUnderflow; Span bytes = stack.PopWord256(); + // If the position is out-of-range, push zero. if (a >= BigInt32) { stack.PushZero(); @@ -443,79 +566,132 @@ public static EvmExceptionType InstructionByte(VirtualMachine _, ref EvmStack st } else { + // Push the extracted byte. stack.PushByte(bytes[adjustedPosition]); } } return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } + /// + /// Implements the SIGNEXTEND opcode. + /// Performs sign extension on a 256-bit integer in-place based on a specified byte index. + /// [SkipLocalsInit] public static EvmExceptionType InstructionSignExtend(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.Low; - if (!stack.PopUInt256(out UInt256 a)) return EvmExceptionType.StackUnderflow; + // Pop the index to determine which byte to use for sign extension. + if (!stack.PopUInt256(out UInt256 a)) + goto StackUnderflow; if (a >= BigInt32) { - if (!stack.EnsureDepth(1)) return EvmExceptionType.StackUnderflow; + // If the index is out-of-range, no extension is needed. + if (!stack.EnsureDepth(1)) + goto StackUnderflow; return EvmExceptionType.None; } int position = 31 - (int)a; + // Peek at the 256-bit word without removing it. Span bytes = stack.PeekWord256(); sbyte sign = (sbyte)bytes[position]; + // Extend the sign by replacing higher-order bytes. if (sign >= 0) { + // Fill with zero bytes. BytesZero32.AsSpan(0, position).CopyTo(bytes[..position]); } else { + // Fill with 0xFF bytes. BytesMax32.AsSpan(0, position).CopyTo(bytes[..position]); } - // Didn't remove from stack so don't need to push back return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } + /// + /// Computes the Keccak-256 hash of a specified memory region. + /// Pops a memory offset and length from the stack, charges gas based on the data size, + /// and pushes the resulting 256-bit hash onto the stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionKeccak256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) goto StackUnderflow; + // Ensure two 256-bit words are available (memory offset and length). + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) + goto StackUnderflow; + + // Deduct gas: base cost plus additional cost per 32-byte word. gasAvailable -= GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in b, out bool outOfGas); - if (outOfGas) goto OutOfGas; + if (outOfGas) + goto OutOfGas; EvmState vmState = vm.EvmState; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) return EvmExceptionType.OutOfGas; + // Charge gas for any required memory expansion. + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) + goto OutOfGas; + // Load the target memory region. Span bytes = vmState.Memory.LoadSpan(in a, b); + // Compute the Keccak-256 hash. KeccakCache.ComputeTo(bytes, out ValueHash256 keccak); + // Push the 256-bit hash result onto the stack. stack.Push32Bytes(in Unsafe.As>(ref keccak)); return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor + // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; StackUnderflow: return EvmExceptionType.StackUnderflow; } + /// + /// Implements the CALLDATALOAD opcode. + /// Loads 32 bytes of call data starting from a position specified on the stack, + /// zero-padding if necessary. + /// [SkipLocalsInit] public static EvmExceptionType InstructionCallDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { gasAvailable -= GasCostOf.VeryLow; - if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; + // Pop the offset from which to load call data. + if (!stack.PopUInt256(out UInt256 result)) + goto StackUnderflow; + // Load 32 bytes from input data, applying zero padding as needed. stack.PushBytes(vm.EvmState.Env.InputData.SliceWithZeroPadding(result, 32)); return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } + /// + /// Calculates and deducts the gas cost for accessing a specific memory region. + /// + /// The current EVM state. + /// The remaining gas available. + /// The starting position in memory. + /// The length of the memory region. + /// true if sufficient gas was available and deducted; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) { + // Calculate additional gas cost for any memory expansion. long memoryCost = vmState.Memory.CalculateMemoryCost(in position, length); if (memoryCost != 0L) { @@ -528,6 +704,12 @@ public static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in return true; } + /// + /// Deducts a specified gas cost from the available gas. + /// + /// The gas cost to deduct. + /// The remaining gas available. + /// true if there was sufficient gas; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool UpdateGas(long gasCost, ref long gasAvailable) { @@ -540,6 +722,11 @@ public static bool UpdateGas(long gasCost, ref long gasAvailable) return true; } + /// + /// Refunds gas by adding the specified amount back to the available gas. + /// + /// The gas amount to refund. + /// The current gas available. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UpdateGasUp(long refund, ref long gasAvailable) { From 47638b53f61ffa57dc010089cf3f78c788559d53 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 05:32:29 +0000 Subject: [PATCH 228/255] Tidy --- ...Jump.cs => EvmInstructions.ControlFlow.cs} | 154 +++++++++++++++ .../EvmInstructions.Environment.cs | 25 +++ .../Instructions/EvmInstructions.cs | 176 ------------------ 3 files changed, 179 insertions(+), 176 deletions(-) rename src/Nethermind/Nethermind.Evm/Instructions/{EvmInstructions.Jump.cs => EvmInstructions.ControlFlow.cs} (54%) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs similarity index 54% rename from src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs rename to src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs index e06fc9b33c0..5d584540bc0 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Jump.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs @@ -4,6 +4,9 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; +using Nethermind.Core.Specs; +using Nethermind.Core; +using Nethermind.State; namespace Nethermind.Evm; using Int256; @@ -121,6 +124,157 @@ public static EvmExceptionType InstructionJumpIf(VirtualMachine vm, ref EvmStack return EvmExceptionType.InvalidJumpDestination; } + /// + /// 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 long gasAvailable, ref int programCounter) + { + // In contract creation contexts, a STOP is not permitted. + if (vm.EvmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) + { + return EvmExceptionType.BadInstruction; + } + + return EvmExceptionType.Stop; + } + + /// + /// Implements the REVERT opcode. + /// Pops a memory offset and length from the stack, updates memory gas cost, loads the return data, + /// and returns a revert exception. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionRevert(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + // Attempt to pop memory offset and length; if either fails, signal a stack underflow. + if (!stack.PopUInt256(out UInt256 position) || + !stack.PopUInt256(out UInt256 length)) + { + goto StackUnderflow; + } + + // Ensure sufficient gas for any required memory expansion. + if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in position, in length)) + { + goto OutOfGas; + } + + // Copy the specified memory region as return data. + vm.ReturnData = vm.EvmState.Memory.Load(in position, in length).ToArray(); + + return EvmExceptionType.Revert; + // Jump forward to be unpredicted by the branch predictor. + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + } + + /// + /// Executes the SELFDESTRUCT opcode. + /// This method handles gas adjustments, account balance transfers, + /// and marks the executing account for destruction. + /// + [SkipLocalsInit] + private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + // Increment metrics for self-destruct operations. + Metrics.IncrementSelfDestructs(); + + EvmState vmState = vm.EvmState; + IReleaseSpec spec = vm.Spec; + IWorldState state = vm.WorldState; + + // SELFDESTRUCT is forbidden during static calls. + if (vmState.IsStatic) + goto StaticCallViolation; + + // If Shanghai DDoS protection is active, charge the appropriate gas cost. + if (spec.UseShanghaiDDosProtection) + { + gasAvailable -= GasCostOf.SelfDestructEip150; + } + + // Pop the inheritor address from the stack; signal underflow if missing. + Address inheritor = stack.PopAddress(); + if (inheritor is null) + goto StackUnderflow; + + // Charge gas for account access; if insufficient, signal out-of-gas. + if (!ChargeAccountAccessGas(ref gasAvailable, vm, inheritor, chargeForWarm: false)) + goto OutOfGas; + + Address executingAccount = vmState.Env.ExecutingAccount; + bool createInSameTx = vmState.AccessTracker.CreateList.Contains(executingAccount); + // Mark the executing account for destruction if allowed. + if (!spec.SelfdestructOnlyOnSameTransaction || createInSameTx) + vmState.AccessTracker.ToBeDestroyed(executingAccount); + + // Retrieve the current balance for transfer. + UInt256 result = state.GetBalance(executingAccount); + if (vm.TxTracer.IsTracingActions) + vm.TxTracer.ReportSelfDestruct(executingAccount, result, inheritor); + + // For certain specs, charge gas if transferring to a dead account. + if (spec.ClearEmptyAccountWhenTouched && !result.IsZero && state.IsDeadAccount(inheritor)) + { + if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) + goto OutOfGas; + } + + // If account creation rules apply, ensure gas is charged for new accounts. + bool inheritorAccountExists = state.AccountExists(inheritor); + if (!spec.ClearEmptyAccountWhenTouched && !inheritorAccountExists && spec.UseShanghaiDDosProtection) + { + if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) + goto OutOfGas; + } + + // Create or update the inheritor account with the transferred balance. + if (!inheritorAccountExists) + { + state.CreateAccount(inheritor, result); + } + else if (!inheritor.Equals(executingAccount)) + { + state.AddToBalance(inheritor, result, spec); + } + + // Special handling when SELFDESTRUCT is limited to the same transaction. + if (spec.SelfdestructOnlyOnSameTransaction && !createInSameTx && inheritor.Equals(executingAccount)) + goto Stop; // Avoid burning ETH if contract is not destroyed per EIP clarification + + // Subtract the balance from the executing account. + state.SubtractFromBalance(executingAccount, result, spec); + + // Jump forward to be unpredicted by the branch predictor. + Stop: + return EvmExceptionType.Stop; + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + StaticCallViolation: + return EvmExceptionType.StaticCallViolation; + } + + /// + /// Handles invalid opcodes by deducting a high gas cost and returning a BadInstruction error. + /// + public static EvmExceptionType InstructionInvalid(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.High; + return EvmExceptionType.BadInstruction; + } + + /// + /// Default handler for undefined opcodes, always returning a BadInstruction error. + /// + public static EvmExceptionType InstructionBadInstruction(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + => EvmExceptionType.BadInstruction; + /// /// Validates a jump destination and, if valid, updates the program counter. /// A valid jump destination must be within the bounds of the code and pass validation rules. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index b1a68c8b8e1..bfa795863d8 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -438,6 +438,31 @@ public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref return EvmExceptionType.StackUnderflow; } + /// + /// Implements the PREVRANDAO opcode. + /// Pushes the previous random value (post-merge) or block difficulty (pre-merge) onto the stack. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionPrevRandao(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + // Charge the base gas cost for this opcode. + gasAvailable -= GasCostOf.Base; + BlockHeader header = vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header; + + // Use the random value if post-merge; otherwise, use block difficulty. + if (header.IsPostMerge) + { + stack.PushBytes(header.Random.Bytes); + } + else + { + UInt256 result = header.Difficulty; + stack.PushUInt256(in result); + } + + return EvmExceptionType.None; + } + /// /// Charges gas for accessing an account, including potential delegation lookups. /// This method ensures that both the requested account and its delegated account (if any) are properly charged. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 2b7cea8fae7..fc45ed8f3e0 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -310,182 +310,6 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) return lookup; } - /// - /// 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 long gasAvailable, ref int programCounter) - { - // In contract creation contexts, a STOP is not permitted. - if (vm.EvmState.ExecutionType is ExecutionType.EOFCREATE or ExecutionType.TXCREATE) - { - return EvmExceptionType.BadInstruction; - } - - return EvmExceptionType.Stop; - } - - /// - /// Implements the REVERT opcode. - /// Pops a memory offset and length from the stack, updates memory gas cost, loads the return data, - /// and returns a revert exception. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionRevert(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - // Attempt to pop memory offset and length; if either fails, signal a stack underflow. - if (!stack.PopUInt256(out UInt256 position) || - !stack.PopUInt256(out UInt256 length)) - { - goto StackUnderflow; - } - - // Ensure sufficient gas for any required memory expansion. - if (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in position, in length)) - { - goto OutOfGas; - } - - // Copy the specified memory region as return data. - vm.ReturnData = vm.EvmState.Memory.Load(in position, in length).ToArray(); - - return EvmExceptionType.Revert; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } - - /// - /// Executes the SELFDESTRUCT opcode. - /// This method handles gas adjustments, account balance transfers, - /// and marks the executing account for destruction. - /// - [SkipLocalsInit] - private static EvmExceptionType InstructionSelfDestruct(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - // Increment metrics for self-destruct operations. - Metrics.IncrementSelfDestructs(); - - EvmState vmState = vm.EvmState; - IReleaseSpec spec = vm.Spec; - IWorldState state = vm.WorldState; - - // SELFDESTRUCT is forbidden during static calls. - if (vmState.IsStatic) - goto StaticCallViolation; - - // If Shanghai DDoS protection is active, charge the appropriate gas cost. - if (spec.UseShanghaiDDosProtection) - { - gasAvailable -= GasCostOf.SelfDestructEip150; - } - - // Pop the inheritor address from the stack; signal underflow if missing. - Address inheritor = stack.PopAddress(); - if (inheritor is null) - goto StackUnderflow; - - // Charge gas for account access; if insufficient, signal out-of-gas. - if (!ChargeAccountAccessGas(ref gasAvailable, vm, inheritor, chargeForWarm: false)) - goto OutOfGas; - - Address executingAccount = vmState.Env.ExecutingAccount; - bool createInSameTx = vmState.AccessTracker.CreateList.Contains(executingAccount); - // Mark the executing account for destruction if allowed. - if (!spec.SelfdestructOnlyOnSameTransaction || createInSameTx) - vmState.AccessTracker.ToBeDestroyed(executingAccount); - - // Retrieve the current balance for transfer. - UInt256 result = state.GetBalance(executingAccount); - if (vm.TxTracer.IsTracingActions) - vm.TxTracer.ReportSelfDestruct(executingAccount, result, inheritor); - - // For certain specs, charge gas if transferring to a dead account. - if (spec.ClearEmptyAccountWhenTouched && !result.IsZero && state.IsDeadAccount(inheritor)) - { - if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) - goto OutOfGas; - } - - // If account creation rules apply, ensure gas is charged for new accounts. - bool inheritorAccountExists = state.AccountExists(inheritor); - if (!spec.ClearEmptyAccountWhenTouched && !inheritorAccountExists && spec.UseShanghaiDDosProtection) - { - if (!UpdateGas(GasCostOf.NewAccount, ref gasAvailable)) - goto OutOfGas; - } - - // Create or update the inheritor account with the transferred balance. - if (!inheritorAccountExists) - { - state.CreateAccount(inheritor, result); - } - else if (!inheritor.Equals(executingAccount)) - { - state.AddToBalance(inheritor, result, spec); - } - - // Special handling when SELFDESTRUCT is limited to the same transaction. - if (spec.SelfdestructOnlyOnSameTransaction && !createInSameTx && inheritor.Equals(executingAccount)) - goto Stop; // Avoid burning ETH if contract is not destroyed per EIP clarification - - // Subtract the balance from the executing account. - state.SubtractFromBalance(executingAccount, result, spec); - - // Jump forward to be unpredicted by the branch predictor. - Stop: - return EvmExceptionType.Stop; - OutOfGas: - return EvmExceptionType.OutOfGas; - StackUnderflow: - return EvmExceptionType.StackUnderflow; - StaticCallViolation: - return EvmExceptionType.StaticCallViolation; - } - - /// - /// Implements the PREVRANDAO opcode. - /// Pushes the previous random value (post-merge) or block difficulty (pre-merge) onto the stack. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionPrevRandao(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - // Charge the base gas cost for this opcode. - gasAvailable -= GasCostOf.Base; - BlockHeader header = vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header; - - // Use the random value if post-merge; otherwise, use block difficulty. - if (header.IsPostMerge) - { - stack.PushBytes(header.Random.Bytes); - } - else - { - UInt256 result = header.Difficulty; - stack.PushUInt256(in result); - } - - return EvmExceptionType.None; - } - - /// - /// Handles invalid opcodes by deducting a high gas cost and returning a BadInstruction error. - /// - public static EvmExceptionType InstructionInvalid(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - gasAvailable -= GasCostOf.High; - return EvmExceptionType.BadInstruction; - } - - /// - /// Default handler for undefined opcodes, always returning a BadInstruction error. - /// - public static EvmExceptionType InstructionBadInstruction(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - => EvmExceptionType.BadInstruction; - /// /// Implements the EXP opcode to perform exponentiation. /// The operation deducts gas based on the size of the exponent and computes the result. From a0507659cc5facfbb291c423629d15f59ae30fa3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 13:06:18 +0000 Subject: [PATCH 229/255] Refactor --- .../Instructions/EvmInstructions.Crypto.cs | 54 ++++ .../EvmInstructions.Environment.cs | 194 +++++++++---- .../Instructions/EvmInstructions.Extras.cs | 132 --------- .../EvmInstructions.Math1Param.cs | 87 ++++++ .../EvmInstructions.Math2Param.cs | 61 +++++ .../Instructions/EvmInstructions.Storage.cs | 57 ++-- .../Instructions/EvmInstructions.cs | 254 ++++++------------ 7 files changed, 449 insertions(+), 390 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs delete mode 100644 src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs new file mode 100644 index 00000000000..c1c43667e5b --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; + +namespace Nethermind.Evm; + +using static Nethermind.Evm.VirtualMachine; +using Int256; + +internal sealed partial class EvmInstructions +{ + + /// + /// Computes the Keccak-256 hash of a specified memory region. + /// Pops a memory offset and length from the stack, charges gas based on the data size, + /// and pushes the resulting 256-bit hash onto the stack. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionKeccak256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + // Ensure two 256-bit words are available (memory offset and length). + if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) + goto StackUnderflow; + + // Deduct gas: base cost plus additional cost per 32-byte word. + gasAvailable -= GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in b, out bool outOfGas); + if (outOfGas) + goto OutOfGas; + + EvmState vmState = vm.EvmState; + // Charge gas for any required memory expansion. + if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) + goto OutOfGas; + + // Load the target memory region. + Span bytes = vmState.Memory.LoadSpan(in a, b); + // Compute the Keccak-256 hash. + KeccakCache.ComputeTo(bytes, out ValueHash256 keccak); + // Push the 256-bit hash result onto the stack. + stack.Push32Bytes(in Unsafe.As>(ref keccak)); + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + OutOfGas: + return EvmExceptionType.OutOfGas; + StackUnderflow: + return EvmExceptionType.StackUnderflow; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index bfa795863d8..b32ddc13cc2 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -5,8 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Diagnostics; using System.Runtime.CompilerServices; -using Nethermind.Core.Specs; using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Core.Crypto; using Nethermind.Evm.EvmObjectFormat; using Nethermind.Evm.Precompiles; using Nethermind.State; @@ -308,6 +309,13 @@ public static void Operation(EvmState vmState, out Span result) /// /// Pushes the chain identifier onto the stack. /// + /// + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// + /// [SkipLocalsInit] public static EvmExceptionType InstructionChainId(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -322,6 +330,15 @@ public static EvmExceptionType InstructionChainId(VirtualMachine vm, ref EvmStac /// Retrieves and pushes the balance of an account. /// The address is popped from the stack. /// + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// + /// if gas is available, + /// if the gas becomes negative + /// or if not enough items on stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -349,6 +366,13 @@ public static EvmExceptionType InstructionBalance(VirtualMachine vm, ref EvmStac /// /// Pushes the balance of the executing account onto the stack. /// + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// + /// + /// [SkipLocalsInit] public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -365,6 +389,15 @@ public static EvmExceptionType InstructionSelfBalance(VirtualMachine vm, ref Evm /// Retrieves the code hash of an external account. /// Returns zero if the account does not exist or is considered dead. /// + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// + /// if gas is available, + /// if the gas becomes negative + /// or if not enough items on stack. + /// [SkipLocalsInit] public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -400,6 +433,15 @@ public static EvmExceptionType InstructionExtCodeHash(VirtualMachine vm, ref Evm /// 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 virtual machine instance. + /// The execution stack where the gas value will be pushed. + /// Reference to the current available gas, which is modified by this operation. + /// 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 long gasAvailable, ref int programCounter) { @@ -442,6 +484,13 @@ public static EvmExceptionType InstructionExtCodeHashEof(VirtualMachine vm, ref /// Implements the PREVRANDAO opcode. /// Pushes the previous random value (post-merge) or block difficulty (pre-merge) onto the stack. /// + /// The virtual machine instance. + /// The execution stack. + /// The available gas which is reduced by the operation's cost. + /// The program counter. + /// + /// + /// [SkipLocalsInit] public static EvmExceptionType InstructionPrevRandao(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { @@ -464,64 +513,119 @@ public static EvmExceptionType InstructionPrevRandao(VirtualMachine vm, ref EvmS } /// - /// Charges gas for accessing an account, including potential delegation lookups. - /// This method ensures that both the requested account and its delegated account (if any) are properly charged. + /// Pushes the remaining gas onto the stack. + /// The gas available is decremented by the base cost, and if negative, an OutOfGas error is returned. + /// + /// The virtual machine instance. + /// The execution stack where the gas value will be pushed. + /// Reference to the current available gas, which is modified by this operation. + /// The current program counter. + /// + /// if gas is available, or if the gas becomes negative. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionGas(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + // Deduct the base gas cost for reading gas. + gasAvailable -= GasCostOf.Base; + + // If gas falls below zero after cost deduction, signal out-of-gas error. + if (gasAvailable < 0) goto OutOfGas; + + // Push the remaining gas (as unsigned 64-bit) onto the stack. + stack.PushUInt64((ulong)gasAvailable); + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + OutOfGas: + return EvmExceptionType.OutOfGas; + } + + /// + /// Computes the blob hash from the provided blob versioned hashes. + /// Pops an index from the stack and uses it to select a blob hash from the versioned hashes array. + /// If the index is invalid, pushes zero. /// - /// Reference to the available gas which will be updated. /// The virtual machine instance. - /// The target account address. - /// If true, charge even if the account is already warm. - /// True if gas was successfully charged; otherwise false. - private static bool ChargeAccountAccessGasWithDelegation(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) + /// The execution stack from which the index is popped and where the blob hash is pushed. + /// Reference to the available gas; reduced by the blob hash cost. + /// The program counter. + /// + /// on success; otherwise, + /// if there are insufficient elements on the stack. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionBlobHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { IReleaseSpec spec = vm.Spec; - if (!spec.UseHotAndColdStorage) + + // Deduct the gas cost for blob hash operation. + gasAvailable -= GasCostOf.BlobHash; + + // Pop the blob index from the stack. + if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + + // Retrieve the array of versioned blob hashes from the execution context. + byte[][] versionedHashes = vm.EvmState.Env.TxExecutionContext.BlobVersionedHashes; + + // If versioned hashes are available and the index is within range, push the corresponding blob hash. + // Otherwise, push zero. + if (versionedHashes is not null && result < versionedHashes.Length) + { + stack.PushBytes(versionedHashes[result.u0]); + } + else { - // No extra cost if hot/cold storage is not used. - return true; + stack.PushZero(); } - bool notOutOfGas = ChargeAccountAccessGas(ref gasAvailable, vm, address, chargeForWarm); - return notOutOfGas - && (!vm.EvmState.Env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(vm.WorldState, address, spec, out Address delegated) - // Charge additional gas for the delegated account if it exists. - || ChargeAccountAccessGas(ref gasAvailable, vm, delegated, chargeForWarm)); + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } /// - /// Charges gas for accessing an account based on its storage state (cold vs. warm). - /// Precompiles are treated as exceptions to the cold/warm gas charge. + /// Retrieves a block hash for a given block number. + /// Pops a block number from the stack, validates it, and then pushes the corresponding block hash. + /// If no valid block hash exists, pushes a zero value. + /// Additionally, reports the block hash if block hash tracing is enabled. /// - /// Reference to the available gas which will be updated. /// The virtual machine instance. - /// The target account address. - /// If true, applies the warm read gas cost even if the account is warm. - /// True if the gas charge was successful; otherwise false. - public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) + /// The execution stack from which the block number is popped and where the block hash is pushed. + /// Reference to the available gas; reduced by the block hash operation cost. + /// The program counter. + /// + /// if the operation completes successfully; + /// otherwise, if there are insufficient stack elements. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - bool result = true; - IReleaseSpec spec = vm.Spec; - if (spec.UseHotAndColdStorage) - { - EvmState vmState = vm.EvmState; - if (vm.TxTracer.IsTracingAccess) - { - // Ensure that tracing simulates access-list behavior. - vmState.AccessTracker.WarmUp(address); - } + // Deduct the gas cost for block hash operation. + gasAvailable -= GasCostOf.BlockHash; - // If the account is cold (and not a precompile), charge the cold access cost. - if (vmState.AccessTracker.IsCold(address) && !address.IsPrecompile(spec)) - { - result = UpdateGas(GasCostOf.ColdAccountAccess, ref gasAvailable); - vmState.AccessTracker.WarmUp(address); - } - else if (chargeForWarm) - { - // Otherwise, if warm access should be charged, apply the warm read cost. - result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); - } + // Pop the block number from the stack. + if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; + + // Convert the block number to a long. Clamp the value to long.MaxValue if it exceeds it. + long number = a > long.MaxValue ? long.MaxValue : (long)a.u0; + + // Retrieve the block hash for the given block number. + Hash256? blockHash = vm.BlockHashProvider.GetBlockhash(vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header, number); + + // Push the block hash bytes if available; otherwise, push a 32-byte zero value. + stack.PushBytes(blockHash is not null ? blockHash.Bytes : BytesZero32); + + // If block hash tracing is enabled and a valid block hash was obtained, report it. + if (vm.TxTracer.IsTracingBlockHash && blockHash is not null) + { + vm.TxTracer.ReportBlockHash(blockHash); } - return result; + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs deleted file mode 100644 index 0b354710657..00000000000 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Extras.cs +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Runtime.CompilerServices; -using Nethermind.Core.Crypto; -using Nethermind.Core.Specs; - -namespace Nethermind.Evm; - -using static Nethermind.Evm.VirtualMachine; -using Int256; - -internal sealed partial class EvmInstructions -{ - /// - /// Pushes the remaining gas onto the stack. - /// The gas available is decremented by the base cost, and if negative, an OutOfGas error is returned. - /// - /// The virtual machine instance. - /// The execution stack where the gas value will be pushed. - /// Reference to the current available gas, which is modified by this operation. - /// The current program counter. - /// - /// if gas is available, or if the gas becomes negative. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionGas(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - // Deduct the base gas cost for reading gas. - gasAvailable -= GasCostOf.Base; - - // If gas falls below zero after cost deduction, signal out-of-gas error. - if (gasAvailable < 0) goto OutOfGas; - - // Push the remaining gas (as unsigned 64-bit) onto the stack. - stack.PushUInt64((ulong)gasAvailable); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - } - - /// - /// Computes the blob hash from the provided blob versioned hashes. - /// Pops an index from the stack and uses it to select a blob hash from the versioned hashes array. - /// If the index is invalid, pushes zero. - /// - /// The virtual machine instance. - /// The execution stack from which the index is popped and where the blob hash is pushed. - /// Reference to the available gas; reduced by the blob hash cost. - /// The program counter. - /// - /// on success; otherwise, - /// if there are insufficient elements on the stack. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionBlobHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - IReleaseSpec spec = vm.Spec; - - // Deduct the gas cost for blob hash operation. - gasAvailable -= GasCostOf.BlobHash; - - // Pop the blob index from the stack. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; - - // Retrieve the array of versioned blob hashes from the execution context. - byte[][] versionedHashes = vm.EvmState.Env.TxExecutionContext.BlobVersionedHashes; - - // If versioned hashes are available and the index is within range, push the corresponding blob hash. - // Otherwise, push zero. - if (versionedHashes is not null && result < versionedHashes.Length) - { - stack.PushBytes(versionedHashes[result.u0]); - } - else - { - stack.PushZero(); - } - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } - - /// - /// Retrieves a block hash for a given block number. - /// Pops a block number from the stack, validates it, and then pushes the corresponding block hash. - /// If no valid block hash exists, pushes a zero value. - /// Additionally, reports the block hash if block hash tracing is enabled. - /// - /// The virtual machine instance. - /// The execution stack from which the block number is popped and where the block hash is pushed. - /// Reference to the available gas; reduced by the block hash operation cost. - /// The program counter. - /// - /// if the operation completes successfully; - /// otherwise, if there are insufficient stack elements. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionBlockHash(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - // Deduct the gas cost for block hash operation. - gasAvailable -= GasCostOf.BlockHash; - - // Pop the block number from the stack. - if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; - - // Convert the block number to a long. Clamp the value to long.MaxValue if it exceeds it. - long number = a > long.MaxValue ? long.MaxValue : (long)a.u0; - - // Retrieve the block hash for the given block number. - Hash256? blockHash = vm.BlockHashProvider.GetBlockhash(vm.EvmState.Env.TxExecutionContext.BlockExecutionContext.Header, number); - - // Push the block hash bytes if available; otherwise, push a 32-byte zero value. - stack.PushBytes(blockHash is not null ? blockHash.Bytes : BytesZero32); - - // If block hash tracing is enabled and a valid block hash was obtained, report it. - if (vm.TxTracer.IsTracingBlockHash && blockHash is not null) - { - vm.TxTracer.ReportBlockHash(blockHash); - } - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } -} diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs index 97f9d1910f3..3cf2f43d3c2 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs @@ -1,9 +1,12 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; +using Nethermind.Int256; using static System.Runtime.CompilerServices.Unsafe; +using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm; using Word = Vector256; @@ -85,4 +88,88 @@ public struct OpIsZero : IOpMath1Param { public static Word Operation(Word value) => value == default ? OpBitwiseEq.One : default; } + + + /// + /// Implements the BYTE opcode. + /// Extracts a byte from a 256-bit word at the position specified by the stack. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionByte(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.VeryLow; + + // Pop the byte position and the 256-bit word. + if (!stack.PopUInt256(out UInt256 a)) + goto StackUnderflow; + Span bytes = stack.PopWord256(); + + // If the position is out-of-range, push zero. + if (a >= BigInt32) + { + stack.PushZero(); + } + else + { + int adjustedPosition = bytes.Length - 32 + (int)a; + if (adjustedPosition < 0) + { + stack.PushZero(); + } + else + { + // Push the extracted byte. + stack.PushByte(bytes[adjustedPosition]); + } + } + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; + } + + /// + /// Implements the SIGNEXTEND opcode. + /// Performs sign extension on a 256-bit integer in-place based on a specified byte index. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionSignExtend(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + gasAvailable -= GasCostOf.Low; + + // Pop the index to determine which byte to use for sign extension. + if (!stack.PopUInt256(out UInt256 a)) + goto StackUnderflow; + if (a >= BigInt32) + { + // If the index is out-of-range, no extension is needed. + if (!stack.EnsureDepth(1)) + goto StackUnderflow; + return EvmExceptionType.None; + } + + int position = 31 - (int)a; + + // Peek at the 256-bit word without removing it. + Span bytes = stack.PeekWord256(); + sbyte sign = (sbyte)bytes[position]; + + // Extend the sign by replacing higher-order bytes. + if (sign >= 0) + { + // Fill with zero bytes. + BytesZero32.AsSpan(0, position).CopyTo(bytes[..position]); + } + else + { + // Fill with 0xFF bytes. + BytesMax32.AsSpan(0, position).CopyTo(bytes[..position]); + } + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; + } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs index 1f4214edaa6..9932fc2d773 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs @@ -1,7 +1,9 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Runtime.CompilerServices; +using Nethermind.Core.Extensions; using static Nethermind.Evm.VirtualMachine; using static System.Runtime.CompilerServices.Unsafe; @@ -241,4 +243,63 @@ public static void Operation(in UInt256 a, in UInt256 b, out UInt256 result) default; } } + + /// + /// Implements the EXP opcode to perform exponentiation. + /// The operation deducts gas based on the size of the exponent and computes the result. + /// + /// The virtual machine instance. + /// The execution stack where the program counter is pushed. + /// Reference to the remaining gas; reduced by the gas cost. + /// The current program counter. + /// + /// on success; or if not enough items on stack. + /// + [SkipLocalsInit] + public static EvmExceptionType InstructionExp(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + { + // Charge the fixed gas cost for exponentiation. + gasAvailable -= GasCostOf.Exp; + + // Pop the base value from the stack. + if (!stack.PopUInt256(out UInt256 a)) + goto StackUnderflow; + + // Pop the exponent as a 256-bit word. + Span bytes = stack.PopWord256(); + + // Determine the effective byte-length of the exponent. + int leadingZeros = bytes.LeadingZerosCount(); + if (leadingZeros == 32) + { + // Exponent is zero, so the result is 1. + stack.PushOne(); + } + else + { + int expSize = 32 - leadingZeros; + // Deduct gas proportional to the number of 32-byte words needed to represent the exponent. + gasAvailable -= vm.Spec.GetExpByteCost() * expSize; + + if (a.IsZero) + { + stack.PushZero(); + } + else if (a.IsOne) + { + stack.PushOne(); + } + else + { + // Perform exponentiation and push the 256-bit result onto the stack. + UInt256.Exp(a, new UInt256(bytes, true), out UInt256 result); + stack.PushUInt256(in result); + } + } + + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; + } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index 7a15c7d958f..0df3142f733 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -569,51 +569,24 @@ internal static EvmExceptionType InstructionSLoad(VirtualMachine vm, ref EvmStac } /// - /// Charges the appropriate gas cost for accessing a storage cell, taking into account whether the access is cold or warm. - /// - /// For cold storage accesses (or if not previously warmed up), a higher gas cost is applied. For warm accesses during SLOAD, - /// a lower cost is deducted. - /// + /// Implements the CALLDATALOAD opcode. + /// Loads 32 bytes of call data starting from a position specified on the stack, + /// zero-padding if necessary. /// - /// The remaining gas, passed by reference and reduced by the access cost. - /// The virtual machine instance. - /// The target storage cell being accessed. - /// Indicates whether the access is for a load (SLOAD) or store (SSTORE) operation. - /// The release specification which governs gas metering and storage access rules. - /// true if the gas charge was successfully applied; otherwise, false indicating an out-of-gas condition. - internal static bool ChargeStorageAccessGas( - ref long gasAvailable, - VirtualMachine vm, - in StorageCell storageCell, - StorageAccessType storageAccessType, - IReleaseSpec spec) + [SkipLocalsInit] + public static EvmExceptionType InstructionCallDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) { - EvmState vmState = vm.EvmState; - bool result = true; + gasAvailable -= GasCostOf.VeryLow; - // If the spec requires hot/cold storage tracking, determine if extra gas should be charged. - if (spec.UseHotAndColdStorage) - { - // When tracing access, ensure the storage cell is marked as warm to simulate inclusion in the access list. - ref readonly StackAccessTracker accessTracker = ref vmState.AccessTracker; - if (vm.TxTracer.IsTracingAccess) - { - accessTracker.WarmUp(in storageCell); - } + // Pop the offset from which to load call data. + if (!stack.PopUInt256(out UInt256 result)) + goto StackUnderflow; + // Load 32 bytes from input data, applying zero padding as needed. + stack.PushBytes(vm.EvmState.Env.InputData.SliceWithZeroPadding(result, 32)); - // If the storage cell is still cold, apply the higher cold access cost and mark it as warm. - if (accessTracker.IsCold(in storageCell)) - { - result = UpdateGas(GasCostOf.ColdSLoad, ref gasAvailable); - accessTracker.WarmUp(in storageCell); - } - // For SLOAD operations on already warmed-up storage, apply a lower warm-read cost. - else if (storageAccessType == StorageAccessType.SLOAD) - { - result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); - } - } - - return result; + return EvmExceptionType.None; + // Jump forward to be unpredicted by the branch predictor. + StackUnderflow: + return EvmExceptionType.StackUnderflow; } } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index fc45ed8f3e0..5186f12cb1a 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -1,19 +1,14 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Runtime.Intrinsics; using System.Runtime.CompilerServices; using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Specs; -using Nethermind.State; -using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm; using unsafe OpCode = delegate*; using Int256; +using Nethermind.Evm.Precompiles; internal unsafe sealed partial class EvmInstructions { @@ -311,197 +306,114 @@ public static OpCode[] GenerateOpCodes(IReleaseSpec spec) } /// - /// Implements the EXP opcode to perform exponentiation. - /// The operation deducts gas based on the size of the exponent and computes the result. + /// Charges gas for accessing an account, including potential delegation lookups. + /// This method ensures that both the requested account and its delegated account (if any) are properly charged. /// - [SkipLocalsInit] - public static EvmExceptionType InstructionExp(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + /// Reference to the available gas which will be updated. + /// The virtual machine instance. + /// The target account address. + /// If true, charge even if the account is already warm. + /// True if gas was successfully charged; otherwise false. + private static bool ChargeAccountAccessGasWithDelegation(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) { - // Charge the fixed gas cost for exponentiation. - gasAvailable -= GasCostOf.Exp; - - // Pop the base value from the stack. - if (!stack.PopUInt256(out UInt256 a)) - goto StackUnderflow; - - // Pop the exponent as a 256-bit word. - Span bytes = stack.PopWord256(); - - // Determine the effective byte-length of the exponent. - int leadingZeros = bytes.LeadingZerosCount(); - if (leadingZeros == 32) + IReleaseSpec spec = vm.Spec; + if (!spec.UseHotAndColdStorage) { - // Exponent is zero, so the result is 1. - stack.PushOne(); - } - else - { - int expSize = 32 - leadingZeros; - // Deduct gas proportional to the number of 32-byte words needed to represent the exponent. - gasAvailable -= vm.Spec.GetExpByteCost() * expSize; - - if (a.IsZero) - { - stack.PushZero(); - } - else if (a.IsOne) - { - stack.PushOne(); - } - else - { - // Perform exponentiation and push the 256-bit result onto the stack. - UInt256.Exp(a, new UInt256(bytes, true), out UInt256 result); - stack.PushUInt256(in result); - } + // No extra cost if hot/cold storage is not used. + return true; } - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; + bool notOutOfGas = ChargeAccountAccessGas(ref gasAvailable, vm, address, chargeForWarm); + return notOutOfGas + && (!vm.EvmState.Env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(vm.WorldState, address, spec, out Address delegated) + // Charge additional gas for the delegated account if it exists. + || ChargeAccountAccessGas(ref gasAvailable, vm, delegated, chargeForWarm)); } /// - /// Implements the BYTE opcode. - /// Extracts a byte from a 256-bit word at the position specified by the stack. + /// Charges gas for accessing an account based on its storage state (cold vs. warm). + /// Precompiles are treated as exceptions to the cold/warm gas charge. /// - [SkipLocalsInit] - public static EvmExceptionType InstructionByte(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + /// Reference to the available gas which will be updated. + /// The virtual machine instance. + /// The target account address. + /// If true, applies the warm read gas cost even if the account is warm. + /// True if the gas charge was successful; otherwise false. + public static bool ChargeAccountAccessGas(ref long gasAvailable, VirtualMachine vm, Address address, bool chargeForWarm = true) { - gasAvailable -= GasCostOf.VeryLow; - - // Pop the byte position and the 256-bit word. - if (!stack.PopUInt256(out UInt256 a)) - goto StackUnderflow; - Span bytes = stack.PopWord256(); - - // If the position is out-of-range, push zero. - if (a >= BigInt32) + bool result = true; + IReleaseSpec spec = vm.Spec; + if (spec.UseHotAndColdStorage) { - stack.PushZero(); - } - else - { - int adjustedPosition = bytes.Length - 32 + (int)a; - if (adjustedPosition < 0) + EvmState vmState = vm.EvmState; + if (vm.TxTracer.IsTracingAccess) { - stack.PushZero(); + // Ensure that tracing simulates access-list behavior. + vmState.AccessTracker.WarmUp(address); } - else + + // If the account is cold (and not a precompile), charge the cold access cost. + if (vmState.AccessTracker.IsCold(address) && !address.IsPrecompile(spec)) { - // Push the extracted byte. - stack.PushByte(bytes[adjustedPosition]); + result = UpdateGas(GasCostOf.ColdAccountAccess, ref gasAvailable); + vmState.AccessTracker.WarmUp(address); + } + else if (chargeForWarm) + { + // Otherwise, if warm access should be charged, apply the warm read cost. + result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); } } - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; + return result; } /// - /// Implements the SIGNEXTEND opcode. - /// Performs sign extension on a 256-bit integer in-place based on a specified byte index. + /// Charges the appropriate gas cost for accessing a storage cell, taking into account whether the access is cold or warm. + /// + /// For cold storage accesses (or if not previously warmed up), a higher gas cost is applied. For warm accesses during SLOAD, + /// a lower cost is deducted. + /// /// - [SkipLocalsInit] - public static EvmExceptionType InstructionSignExtend(VirtualMachine _, ref EvmStack stack, ref long gasAvailable, ref int programCounter) + /// The remaining gas, passed by reference and reduced by the access cost. + /// The virtual machine instance. + /// The target storage cell being accessed. + /// Indicates whether the access is for a load (SLOAD) or store (SSTORE) operation. + /// The release specification which governs gas metering and storage access rules. + /// true if the gas charge was successfully applied; otherwise, false indicating an out-of-gas condition. + internal static bool ChargeStorageAccessGas( + ref long gasAvailable, + VirtualMachine vm, + in StorageCell storageCell, + StorageAccessType storageAccessType, + IReleaseSpec spec) { - gasAvailable -= GasCostOf.Low; + EvmState vmState = vm.EvmState; + bool result = true; - // Pop the index to determine which byte to use for sign extension. - if (!stack.PopUInt256(out UInt256 a)) - goto StackUnderflow; - if (a >= BigInt32) + // If the spec requires hot/cold storage tracking, determine if extra gas should be charged. + if (spec.UseHotAndColdStorage) { - // If the index is out-of-range, no extension is needed. - if (!stack.EnsureDepth(1)) - goto StackUnderflow; - return EvmExceptionType.None; - } - - int position = 31 - (int)a; - - // Peek at the 256-bit word without removing it. - Span bytes = stack.PeekWord256(); - sbyte sign = (sbyte)bytes[position]; + // When tracing access, ensure the storage cell is marked as warm to simulate inclusion in the access list. + ref readonly StackAccessTracker accessTracker = ref vmState.AccessTracker; + if (vm.TxTracer.IsTracingAccess) + { + accessTracker.WarmUp(in storageCell); + } - // Extend the sign by replacing higher-order bytes. - if (sign >= 0) - { - // Fill with zero bytes. - BytesZero32.AsSpan(0, position).CopyTo(bytes[..position]); - } - else - { - // Fill with 0xFF bytes. - BytesMax32.AsSpan(0, position).CopyTo(bytes[..position]); + // If the storage cell is still cold, apply the higher cold access cost and mark it as warm. + if (accessTracker.IsCold(in storageCell)) + { + result = UpdateGas(GasCostOf.ColdSLoad, ref gasAvailable); + accessTracker.WarmUp(in storageCell); + } + // For SLOAD operations on already warmed-up storage, apply a lower warm-read cost. + else if (storageAccessType == StorageAccessType.SLOAD) + { + result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); + } } - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } - - /// - /// Computes the Keccak-256 hash of a specified memory region. - /// Pops a memory offset and length from the stack, charges gas based on the data size, - /// and pushes the resulting 256-bit hash onto the stack. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionKeccak256(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - // Ensure two 256-bit words are available (memory offset and length). - if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) - goto StackUnderflow; - - // Deduct gas: base cost plus additional cost per 32-byte word. - gasAvailable -= GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmPooledMemory.Div32Ceiling(in b, out bool outOfGas); - if (outOfGas) - goto OutOfGas; - - EvmState vmState = vm.EvmState; - // Charge gas for any required memory expansion. - if (!UpdateMemoryCost(vmState, ref gasAvailable, in a, b)) - goto OutOfGas; - - // Load the target memory region. - Span bytes = vmState.Memory.LoadSpan(in a, b); - // Compute the Keccak-256 hash. - KeccakCache.ComputeTo(bytes, out ValueHash256 keccak); - // Push the 256-bit hash result onto the stack. - stack.Push32Bytes(in Unsafe.As>(ref keccak)); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - OutOfGas: - return EvmExceptionType.OutOfGas; - StackUnderflow: - return EvmExceptionType.StackUnderflow; - } - - /// - /// Implements the CALLDATALOAD opcode. - /// Loads 32 bytes of call data starting from a position specified on the stack, - /// zero-padding if necessary. - /// - [SkipLocalsInit] - public static EvmExceptionType InstructionCallDataLoad(VirtualMachine vm, ref EvmStack stack, ref long gasAvailable, ref int programCounter) - { - gasAvailable -= GasCostOf.VeryLow; - - // Pop the offset from which to load call data. - if (!stack.PopUInt256(out UInt256 result)) - goto StackUnderflow; - // Load 32 bytes from input data, applying zero padding as needed. - stack.PushBytes(vm.EvmState.Env.InputData.SliceWithZeroPadding(result, 32)); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; + return result; } /// From e7aeb8453f07ee625a434736d7e2e096d83ede94 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 13:24:44 +0000 Subject: [PATCH 230/255] merge conflict --- .../Filters/OnlyOneTxPerDelegatedAccountFilter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.TxPool/Filters/OnlyOneTxPerDelegatedAccountFilter.cs b/src/Nethermind/Nethermind.TxPool/Filters/OnlyOneTxPerDelegatedAccountFilter.cs index e45f477e245..17c994fba09 100644 --- a/src/Nethermind/Nethermind.TxPool/Filters/OnlyOneTxPerDelegatedAccountFilter.cs +++ b/src/Nethermind/Nethermind.TxPool/Filters/OnlyOneTxPerDelegatedAccountFilter.cs @@ -25,7 +25,7 @@ public AcceptTxResult Accept(Transaction tx, ref TxFilteringState state, TxHandl if (pendingDelegations.HasPending(tx.SenderAddress!, tx.Nonce)) return AcceptTxResult.PendingDelegation; - if (!codeInfoRepository.TryGetDelegation(worldState, tx.SenderAddress!, out _)) + if (!codeInfoRepository.TryGetDelegation(worldState, tx.SenderAddress!, spec, out _)) return AcceptTxResult.Accepted; //Transactios from the same source can only be either blob transactions or other type if (tx.SupportsBlobs ? !blobPool.BucketEmptyExcept(tx.SenderAddress!, (t) => t.Nonce == tx.Nonce) From 990f049057c5224ba11a570a65cf943675a8a660 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 4 Feb 2025 13:28:07 +0000 Subject: [PATCH 231/255] merge conflict --- .../OnlyOneTxPerDelegatedAccountFilter.cs | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/Nethermind/Nethermind.TxPool/Filters/OnlyOneTxPerDelegatedAccountFilter.cs b/src/Nethermind/Nethermind.TxPool/Filters/OnlyOneTxPerDelegatedAccountFilter.cs index 17c994fba09..3822b589443 100644 --- a/src/Nethermind/Nethermind.TxPool/Filters/OnlyOneTxPerDelegatedAccountFilter.cs +++ b/src/Nethermind/Nethermind.TxPool/Filters/OnlyOneTxPerDelegatedAccountFilter.cs @@ -1,39 +1,36 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm; -using Nethermind.Int256; using Nethermind.State; using Nethermind.TxPool.Collections; -using System.Collections.Concurrent; -namespace Nethermind.TxPool.Filters +namespace Nethermind.TxPool.Filters; + +internal sealed class OnlyOneTxPerDelegatedAccountFilter( + IChainHeadSpecProvider specProvider, + TxDistinctSortedPool standardPool, + TxDistinctSortedPool blobPool, + IReadOnlyStateProvider worldState, + ICodeInfoRepository codeInfoRepository, + DelegationCache pendingDelegations) : IIncomingTxFilter { - internal sealed class OnlyOneTxPerDelegatedAccountFilter( - IChainHeadSpecProvider specProvider, - TxDistinctSortedPool standardPool, - TxDistinctSortedPool blobPool, - IReadOnlyStateProvider worldState, - ICodeInfoRepository codeInfoRepository, - DelegationCache pendingDelegations) : IIncomingTxFilter + public AcceptTxResult Accept(Transaction tx, ref TxFilteringState state, TxHandlingOptions txHandlingOptions) { - public AcceptTxResult Accept(Transaction tx, ref TxFilteringState state, TxHandlingOptions txHandlingOptions) - { - IReleaseSpec spec = specProvider.GetCurrentHeadSpec(); - if (!spec.IsEip7702Enabled) - return AcceptTxResult.Accepted; + IReleaseSpec spec = specProvider.GetCurrentHeadSpec(); + if (!spec.IsEip7702Enabled) + return AcceptTxResult.Accepted; - if (pendingDelegations.HasPending(tx.SenderAddress!, tx.Nonce)) - return AcceptTxResult.PendingDelegation; + if (pendingDelegations.HasPending(tx.SenderAddress!, tx.Nonce)) + return AcceptTxResult.PendingDelegation; - if (!codeInfoRepository.TryGetDelegation(worldState, tx.SenderAddress!, spec, out _)) - return AcceptTxResult.Accepted; - //Transactios from the same source can only be either blob transactions or other type - if (tx.SupportsBlobs ? !blobPool.BucketEmptyExcept(tx.SenderAddress!, (t) => t.Nonce == tx.Nonce) - : !standardPool.BucketEmptyExcept(tx.SenderAddress!, (t) => t.Nonce == tx.Nonce)) - { - return AcceptTxResult.MoreThanOneTxPerDelegatedAccount; - } + if (!codeInfoRepository.TryGetDelegation(worldState, tx.SenderAddress!, spec, out _)) return AcceptTxResult.Accepted; + // Transactions from the same source can only be either blob transactions or other type + if (tx.SupportsBlobs ? !blobPool.BucketEmptyExcept(tx.SenderAddress!, (t) => t.Nonce == tx.Nonce) + : !standardPool.BucketEmptyExcept(tx.SenderAddress!, (t) => t.Nonce == tx.Nonce)) + { + return AcceptTxResult.MoreThanOneTxPerDelegatedAccount; } + return AcceptTxResult.Accepted; } } From 222cb44289ec048ae0c436023df3b1c52ec54b36 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 01:33:41 +0000 Subject: [PATCH 232/255] Tidy up --- .../Nethermind.Evm/VirtualMachine.cs | 237 ++++++++++-------- 1 file changed, 135 insertions(+), 102 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 5558bf7bf7a..d6e256df9d6 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -112,31 +112,16 @@ public TransactionSubstate Run(EvmState state, IWorldState _txTracer = txTracer; _state = worldState; - _spec = _specProvider.GetSpec(state.Env.TxExecutionContext.BlockExecutionContext.Header.Number, state.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp); - if (!TTracingInstructions.IsActive) - { - if (_txCount < 500_000 && Interlocked.Increment(ref _txCount) % 10_000 == 0) - { - if (_logger.IsDebug) _logger.Debug("Resetting EVM instructions cache"); - // Flush the cache every 10_000 transactions to directly point at any PGO optimized methods rather than via pre-stubs - // May be a few cycles to pick up pointers to the optimized methods depending on what's in the blocks, - // however the the refreshes don't take long. - _spec.EvmInstructions = EvmInstructions.GenerateOpCodes(_spec); - } - _opcodeMethods = (OpCode[])(_spec.EvmInstructions ??= EvmInstructions.GenerateOpCodes(_spec)); - } - else - { - _opcodeMethods = (OpCode[])(_spec.EvmTracedInstructions ??= EvmInstructions.GenerateOpCodes(_spec)); - } ref readonly TxExecutionContext txExecutionContext = ref state.Env.TxExecutionContext; + IReleaseSpec spec = PrepareSpecAndOpcodes(txExecutionContext.BlockExecutionContext.Header); + ICodeInfoRepository codeInfoRepository = txExecutionContext.CodeInfoRepository; - IReleaseSpec spec = _specProvider.GetSpec(txExecutionContext.BlockExecutionContext.Header.Number, txExecutionContext.BlockExecutionContext.Header.Timestamp); EvmState currentState = state; ReadOnlyMemory? previousCallResult = null; ZeroPaddedSpan previousCallOutput = ZeroPaddedSpan.Empty; UInt256 previousCallOutputDestination = UInt256.Zero; bool isTracing = _txTracer.IsTracing; + bool isTracingActions = _txTracer.IsTracingActions; while (true) { @@ -151,45 +136,17 @@ public TransactionSubstate Run(EvmState state, IWorldState CallResult callResult; if (currentState.IsPrecompile) { - if (_txTracer.IsTracingActions) + callResult = ExecutePrecompile(currentState, isTracingActions, out failure); + if (failure is not null) { - _txTracer.ReportAction(currentState.GasAvailable, currentState.Env.Value, currentState.From, currentState.To, currentState.Env.InputData, currentState.ExecutionType, true); - } - - callResult = ExecutePrecompile(currentState); - - if (!callResult.PrecompileSuccess.Value) - { - if (callResult.IsException) - { - failure = VirtualMachine.PrecompileOutOfGasException; - goto Failure; - } - if (currentState.IsPrecompile && currentState.IsTopLevel) - { - failure = VirtualMachine.PrecompileExecutionFailureException; - // TODO: when direct / calls are treated same we should not need such differentiation - goto Failure; - } - - // TODO: testing it as it seems the way to pass zkSNARKs tests - currentState.GasAvailable = 0; + goto Failure; } } else { - if (_txTracer.IsTracingActions && !currentState.IsContinuation) + if (isTracingActions && !currentState.IsContinuation) { - _txTracer.ReportAction(currentState.GasAvailable, - currentState.Env.Value, - currentState.From, - currentState.To, - currentState.ExecutionType.IsAnyCreate() - ? currentState.Env.CodeInfo.MachineCode - : currentState.Env.InputData, - currentState.ExecutionType); - - if (_txTracer.IsTracingCode) _txTracer.ReportByteCode(currentState.Env.CodeInfo?.MachineCode ?? default); + TraceTransactionActionStart(currentState); } if (currentState.Env.CodeInfo is not null) @@ -213,7 +170,7 @@ public TransactionSubstate Run(EvmState state, IWorldState if (callResult.IsException) { - if (_txTracer.IsTracingActions) _txTracer.ReportActionError(callResult.ExceptionType); + if (isTracingActions) _txTracer.ReportActionError(callResult.ExceptionType); _state.Restore(currentState.Snapshot); RevertParityTouchBugAccount(); @@ -237,48 +194,9 @@ public TransactionSubstate Run(EvmState state, IWorldState if (currentState.IsTopLevel) { - if (_txTracer.IsTracingActions) + if (isTracingActions) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); - - if (callResult.IsException) - { - _txTracer.ReportActionError(callResult.ExceptionType); - } - else if (callResult.ShouldRevert) - { - _txTracer.ReportActionRevert(currentState.ExecutionType.IsAnyCreate() - ? currentState.GasAvailable - codeDepositGasCost - : currentState.GasAvailable, - callResult.Output.Bytes); - } - else if (currentState.ExecutionType.IsAnyCreate()) - { - if (currentState.GasAvailable < codeDepositGasCost) - { - if (spec.ChargeForTopLevelCreate) - { - _txTracer.ReportActionError(EvmExceptionType.OutOfGas); - } - else - { - _txTracer.ReportActionEnd(currentState.GasAvailable, currentState.To, callResult.Output.Bytes); - } - } - // Reject code starting with 0xEF if EIP-3541 is enabled. - else if (CodeDepositHandler.CodeIsInvalid(spec, callResult.Output.Bytes, callResult.FromVersion)) - { - _txTracer.ReportActionError(EvmExceptionType.InvalidCode); - } - else - { - _txTracer.ReportActionEnd(currentState.GasAvailable - codeDepositGasCost, currentState.To, callResult.Output.Bytes); - } - } - else - { - _txTracer.ReportActionEnd(currentState.GasAvailable, _returnDataBuffer); - } + TraceTransactionActionEnd(currentState, spec, callResult); } return new TransactionSubstate( @@ -318,7 +236,7 @@ public TransactionSubstate Run(EvmState state, IWorldState codeInfoRepository.InsertCode(_state, code, callCodeOwner, spec); currentState.GasAvailable -= codeDepositGasCost; - if (_txTracer.IsTracingActions) + if (isTracingActions) { _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); } @@ -335,12 +253,12 @@ public TransactionSubstate Run(EvmState state, IWorldState previousCallResult = BytesZero; previousStateSucceeded = false; - if (_txTracer.IsTracingActions) + if (isTracingActions) { _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); } } - else if (_txTracer.IsTracingActions) + else if (isTracingActions) { _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); } @@ -386,7 +304,7 @@ public TransactionSubstate Run(EvmState state, IWorldState codeInfoRepository.InsertCode(_state, bytecodeResultArray, callCodeOwner, spec); currentState.GasAvailable -= codeDepositGasCost; - if (_txTracer.IsTracingActions) + if (isTracingActions) { _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, bytecodeResultArray); } @@ -403,12 +321,12 @@ public TransactionSubstate Run(EvmState state, IWorldState previousCallResult = BytesZero; previousStateSucceeded = false; - if (_txTracer.IsTracingActions) + if (isTracingActions) { _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); } } - else if (_txTracer.IsTracingActions) + else if (isTracingActions) { _txTracer.ReportActionEnd(0L, callCodeOwner, bytecodeResultArray); } @@ -432,7 +350,7 @@ public TransactionSubstate Run(EvmState state, IWorldState } } - if (_txTracer.IsTracingActions) + if (isTracingActions) { _txTracer.ReportActionEnd(previousState.GasAvailable, _returnDataBuffer); } @@ -454,7 +372,7 @@ public TransactionSubstate Run(EvmState state, IWorldState previousCallOutputDestination = (ulong)previousState.OutputDestination; - if (_txTracer.IsTracingActions) + if (isTracingActions) { _txTracer.ReportActionRevert(previousState.GasAvailable, callResult.Output.Bytes); } @@ -483,7 +401,7 @@ public TransactionSubstate Run(EvmState state, IWorldState txTracer.ReportOperationError(failure is EvmException evmException ? evmException.ExceptionType : EvmExceptionType.Other); } - if (_txTracer.IsTracingActions) + if (isTracingActions) { EvmException evmException = failure as EvmException; _txTracer.ReportActionError(evmException?.ExceptionType ?? EvmExceptionType.Other); @@ -506,6 +424,121 @@ public TransactionSubstate Run(EvmState state, IWorldState } } + private CallResult ExecutePrecompile(EvmState currentState, bool isTracingActions, out Exception? failure) + { + CallResult callResult; + if (isTracingActions) + { + _txTracer.ReportAction(currentState.GasAvailable, currentState.Env.Value, currentState.From, currentState.To, currentState.Env.InputData, currentState.ExecutionType, true); + } + + callResult = ExecutePrecompile(currentState); + + if (!callResult.PrecompileSuccess.Value) + { + if (callResult.IsException) + { + failure = VirtualMachine.PrecompileOutOfGasException; + goto Failure; + } + if (currentState.IsPrecompile && currentState.IsTopLevel) + { + failure = VirtualMachine.PrecompileExecutionFailureException; + // TODO: when direct / calls are treated same we should not need such differentiation + goto Failure; + } + + // TODO: testing it as it seems the way to pass zkSNARKs tests + currentState.GasAvailable = 0; + } + + failure = null; + return callResult; + Failure: + return default; + } + + private void TraceTransactionActionStart(EvmState currentState) + { + _txTracer.ReportAction(currentState.GasAvailable, + currentState.Env.Value, + currentState.From, + currentState.To, + currentState.ExecutionType.IsAnyCreate() + ? currentState.Env.CodeInfo.MachineCode + : currentState.Env.InputData, + currentState.ExecutionType); + + if (_txTracer.IsTracingCode) _txTracer.ReportByteCode(currentState.Env.CodeInfo?.MachineCode ?? default); + } + + private IReleaseSpec PrepareSpecAndOpcodes(BlockHeader header) where TTracingInstructions : struct, IFlag + { + IReleaseSpec spec = _specProvider.GetSpec(header.Number, header.Timestamp); + if (!TTracingInstructions.IsActive) + { + if (_txCount < 500_000 && Interlocked.Increment(ref _txCount) % 10_000 == 0) + { + if (_logger.IsDebug) _logger.Debug("Resetting EVM instructions cache"); + // Flush the cache every 10_000 transactions to directly point at any PGO optimized methods rather than via pre-stubs + // May be a few cycles to pick up pointers to the optimized methods depending on what's in the blocks, + // however the the refreshes don't take long. + spec.EvmInstructions = EvmInstructions.GenerateOpCodes(spec); + } + _opcodeMethods = (OpCode[])(spec.EvmInstructions ??= EvmInstructions.GenerateOpCodes(spec)); + } + else + { + _opcodeMethods = (OpCode[])(spec.EvmTracedInstructions ??= EvmInstructions.GenerateOpCodes(spec)); + } + + return (_spec = spec); + } + + private void TraceTransactionActionEnd(EvmState currentState, IReleaseSpec spec, in CallResult callResult) + { + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); + + if (callResult.IsException) + { + _txTracer.ReportActionError(callResult.ExceptionType); + } + else if (callResult.ShouldRevert) + { + _txTracer.ReportActionRevert(currentState.ExecutionType.IsAnyCreate() + ? currentState.GasAvailable - codeDepositGasCost + : currentState.GasAvailable, + callResult.Output.Bytes); + } + else if (currentState.ExecutionType.IsAnyCreate()) + { + if (currentState.GasAvailable < codeDepositGasCost) + { + if (spec.ChargeForTopLevelCreate) + { + _txTracer.ReportActionError(EvmExceptionType.OutOfGas); + } + else + { + _txTracer.ReportActionEnd(currentState.GasAvailable, currentState.To, callResult.Output.Bytes); + } + } + // Reject code starting with 0xEF if EIP-3541 is enabled. + else if (CodeDepositHandler.CodeIsInvalid(spec, callResult.Output.Bytes, callResult.FromVersion)) + { + _txTracer.ReportActionError(EvmExceptionType.InvalidCode); + } + else + { + _txTracer.ReportActionEnd(currentState.GasAvailable - codeDepositGasCost, currentState.To, callResult.Output.Bytes); + } + } + else + { + _txTracer.ReportActionEnd(currentState.GasAvailable, _returnDataBuffer); + } + } + private void RevertParityTouchBugAccount() { if (_parityTouchBugAccount.ShouldDelete) From 19dd6f2c9f5928288acf8d9b8d985043de12c232 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 03:20:42 +0000 Subject: [PATCH 233/255] Refactor --- .../Nethermind.Evm/VirtualMachine.cs | 510 ++++++++++-------- 1 file changed, 286 insertions(+), 224 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d6e256df9d6..086129b4300 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -20,6 +20,7 @@ using Nethermind.State; using static Nethermind.Evm.EvmObjectFormat.EofValidator; +using static Nethermind.Evm.VirtualMachine; #if DEBUG using Nethermind.Evm.Tracing.Debugger; @@ -67,18 +68,18 @@ public sealed unsafe class VirtualMachine : IVirtualMachine private readonly ISpecProvider _specProvider; private readonly ILogger _logger; private readonly Stack _stateStack = new(); - private readonly ICodeInfoRepository _codeInfoRepository; - private IWorldState _state = null!; + private IWorldState _worldState = null!; private (Address Address, bool ShouldDelete) _parityTouchBugAccount = (Address.FromNumber(3), false); private ReadOnlyMemory _returnDataBuffer = Array.Empty(); private ITxTracer _txTracer = NullTxTracer.Instance; private IReleaseSpec _spec; + private ICodeInfoRepository _codeInfoRepository; public ICodeInfoRepository CodeInfoRepository => _codeInfoRepository; public IReleaseSpec Spec => _spec; public ITxTracer TxTracer => _txTracer; - public IWorldState WorldState => _state; + public IWorldState WorldState => _worldState; public ReadOnlySpan ChainId => _chainId; public ReadOnlyMemory ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } public object ReturnData { get => _returnData; set => _returnData = value; } @@ -93,6 +94,10 @@ public sealed unsafe class VirtualMachine : IVirtualMachine private OpCode[] _opcodeMethods; private static long _txCount; + private EvmState _currentState; + private ReadOnlyMemory? _previousCallResult; + private UInt256 _previousCallOutputDestination; + public VirtualMachine( IBlockhashProvider? blockHashProvider, ISpecProvider? specProvider, @@ -106,37 +111,35 @@ public VirtualMachine( _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); } - public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) + public TransactionSubstate Run(EvmState evmState, IWorldState worldState, ITxTracer txTracer) where TTracingInstructions : struct, IFlag { _txTracer = txTracer; - _state = worldState; + _worldState = worldState; - ref readonly TxExecutionContext txExecutionContext = ref state.Env.TxExecutionContext; + ref readonly TxExecutionContext txExecutionContext = ref evmState.Env.TxExecutionContext; IReleaseSpec spec = PrepareSpecAndOpcodes(txExecutionContext.BlockExecutionContext.Header); - ICodeInfoRepository codeInfoRepository = txExecutionContext.CodeInfoRepository; - EvmState currentState = state; - ReadOnlyMemory? previousCallResult = null; + _codeInfoRepository = txExecutionContext.CodeInfoRepository; + _currentState = evmState; + _previousCallResult = null; + _previousCallOutputDestination = UInt256.Zero; ZeroPaddedSpan previousCallOutput = ZeroPaddedSpan.Empty; - UInt256 previousCallOutputDestination = UInt256.Zero; - bool isTracing = _txTracer.IsTracing; - bool isTracingActions = _txTracer.IsTracingActions; while (true) { - Exception? failure = null; - if (!currentState.IsContinuation) + if (!_currentState.IsContinuation) { _returnDataBuffer = Array.Empty(); } + Exception? failure; try { CallResult callResult; - if (currentState.IsPrecompile) + if (_currentState.IsPrecompile) { - callResult = ExecutePrecompile(currentState, isTracingActions, out failure); + callResult = ExecutePrecompile(_currentState, _txTracer.IsTracingActions, out failure); if (failure is not null) { goto Failure; @@ -144,14 +147,14 @@ public TransactionSubstate Run(EvmState state, IWorldState } else { - if (isTracingActions && !currentState.IsContinuation) + if (_txTracer.IsTracingActions && !_currentState.IsContinuation) { - TraceTransactionActionStart(currentState); + TraceTransactionActionStart(_currentState); } - if (currentState.Env.CodeInfo is not null) + if (_currentState.Env.CodeInfo is not null) { - callResult = ExecuteCall(currentState, previousCallResult, previousCallOutput, previousCallOutputDestination); + callResult = ExecuteCall(_currentState, _previousCallResult, previousCallOutput, _previousCallOutputDestination); } else { @@ -160,222 +163,66 @@ public TransactionSubstate Run(EvmState state, IWorldState if (!callResult.IsReturn) { - _stateStack.Push(currentState); - currentState = callResult.StateToExecute; - previousCallResult = null; // TODO: testing on ropsten sync, write VirtualMachineTest for this case as it was not covered by Ethereum tests (failing block 9411 on Ropsten https://ropsten.etherscan.io/vmtrace?txhash=0x666194d15c14c54fffafab1a04c08064af165870ef9a87f65711dcce7ed27fe1) - _returnDataBuffer = Array.Empty(); - previousCallOutput = ZeroPaddedSpan.Empty; + PrepareNextCallFrame(in callResult, ref previousCallOutput); continue; } if (callResult.IsException) { - if (isTracingActions) _txTracer.ReportActionError(callResult.ExceptionType); - _state.Restore(currentState.Snapshot); - - RevertParityTouchBugAccount(); - - if (currentState.IsTopLevel) + TransactionSubstate? substate = HandleException(in callResult, ref previousCallOutput); + if (substate is not null) { - return new TransactionSubstate(callResult.ExceptionType, isTracing); + return substate; } - - previousCallResult = currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; - previousCallOutputDestination = UInt256.Zero; - _returnDataBuffer = Array.Empty(); - previousCallOutput = ZeroPaddedSpan.Empty; - - currentState.Dispose(); - currentState = _stateStack.Pop(); - currentState.IsContinuation = true; continue; } } - if (currentState.IsTopLevel) + if (_currentState.IsTopLevel) { - if (isTracingActions) + if (_txTracer.IsTracingActions) { - TraceTransactionActionEnd(currentState, spec, callResult); + TraceTransactionActionEnd(_currentState, spec, callResult); } - return new TransactionSubstate( - callResult.Output, - currentState.Refund, - currentState.AccessTracker.DestroyList, - (IReadOnlyCollection)currentState.AccessTracker.Logs, - callResult.ShouldRevert, - isTracerConnected: isTracing, - _logger); + return PrepareTopLevelSubstate(in callResult); } - Address callCodeOwner = currentState.Env.ExecutingAccount; - using (EvmState previousState = currentState) + using (EvmState previousState = _currentState) { - currentState = _stateStack.Pop(); - currentState.IsContinuation = true; - currentState.GasAvailable += previousState.GasAvailable; + _currentState = _stateStack.Pop(); + _currentState.IsContinuation = true; + _currentState.GasAvailable += previousState.GasAvailable; bool previousStateSucceeded = true; if (!callResult.ShouldRevert) { - long gasAvailableForCodeDeposit = previousState.GasAvailable; // TODO: refactor, this is to fix 61363 Ropsten + long gasAvailableForCodeDeposit = previousState.GasAvailable; if (previousState.ExecutionType.IsAnyCreate()) { - previousCallResult = callCodeOwner.Bytes; - previousCallOutputDestination = UInt256.Zero; - _returnDataBuffer = Array.Empty(); - previousCallOutput = ZeroPaddedSpan.Empty; + PrepareCreateData(previousState, ref previousCallOutput); if (previousState.ExecutionType.IsAnyCreateLegacy()) { - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); - bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(spec, callResult.Output.Bytes); - if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) - { - ReadOnlyMemory code = callResult.Output.Bytes; - codeInfoRepository.InsertCode(_state, code, callCodeOwner, spec); - currentState.GasAvailable -= codeDepositGasCost; - - if (isTracingActions) - { - _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); - } - } - else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) - { - currentState.GasAvailable -= gasAvailableForCodeDeposit; - worldState.Restore(previousState.Snapshot); - if (!previousState.IsCreateOnPreExistingAccount) - { - _state.DeleteAccount(callCodeOwner); - } - - previousCallResult = BytesZero; - previousStateSucceeded = false; - - if (isTracingActions) - { - _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); - } - } - else if (isTracingActions) - { - _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); - } + HandleLegacyCreate(in callResult, previousState, gasAvailableForCodeDeposit, spec, ref previousStateSucceeded); } else if (previousState.ExecutionType.IsAnyCreateEof()) { - // ReturnContract was called with a container index and auxdata - // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed - ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; - EofCodeInfo deployCodeInfo = (EofCodeInfo)callResult.Output.Container; - byte[] bytecodeResultArray = null; - - // 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.MachineCode.Length + auxExtraData.Length]; - // 2 - 1 - 1 - copy old container - deployCodeInfo.MachineCode.Span.CopyTo(bytecodeResult); - // 2 - 1 - 2 - copy aux data to dataSection - auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.Length..]); - - // 2 - 2 - update data section size in the header u16 - int dataSubheaderSectionStart = - EofValidator.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) - + (deployCodeInfo.EofContainer.Header.ContainerSections is null - ? 0 // container section : (0 bytes if no container section is available) - : ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) - + ONE_BYTE_LENGTH; // data section seperator - - ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); - bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); - bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); - - bytecodeResultArray = bytecodeResult.ToArray(); - - // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts - bool invalidCode = bytecodeResultArray.Length > spec.MaxCodeSize; - long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, bytecodeResultArray?.Length ?? 0); - if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) - { - // 4 - set state[new_address].code to the updated deploy container - // push new_address onto the stack (already done before the ifs) - codeInfoRepository.InsertCode(_state, bytecodeResultArray, callCodeOwner, spec); - currentState.GasAvailable -= codeDepositGasCost; - - if (isTracingActions) - { - _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, bytecodeResultArray); - } - } - else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) - { - currentState.GasAvailable -= gasAvailableForCodeDeposit; - worldState.Restore(previousState.Snapshot); - if (!previousState.IsCreateOnPreExistingAccount) - { - _state.DeleteAccount(callCodeOwner); - } - - previousCallResult = BytesZero; - previousStateSucceeded = false; - - if (isTracingActions) - { - _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); - } - } - else if (isTracingActions) - { - _txTracer.ReportActionEnd(0L, callCodeOwner, bytecodeResultArray); - } + HandleEofCreate(in callResult, previousState, gasAvailableForCodeDeposit, spec, ref previousStateSucceeded); } } else { - _returnDataBuffer = callResult.Output.Bytes; - previousCallResult = previousState.ExecutionType.IsAnyCallEof() ? EofStatusCode.SuccessBytes : - 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)); - previousCallOutputDestination = (ulong)previousState.OutputDestination; - if (previousState.IsPrecompile) - { - // parity induced if else for vmtrace - if (TTracingInstructions.IsActive) - { - _txTracer.ReportMemoryChange(previousCallOutputDestination, previousCallOutput); - } - } - - if (isTracingActions) - { - _txTracer.ReportActionEnd(previousState.GasAvailable, _returnDataBuffer); - } + previousCallOutput = HandleRegularReturn(in callResult, previousState); } if (previousStateSucceeded) { - previousState.CommitToParent(currentState); + previousState.CommitToParent(_currentState); } } else { - worldState.Restore(previousState.Snapshot); - _returnDataBuffer = callResult.Output.Bytes; - previousCallResult = previousState.ExecutionType.IsAnyCallEof() - ? (callResult.PrecompileSuccess is not null ? EofStatusCode.FailureBytes : EofStatusCode.RevertBytes) - : StatusCode.FailureBytes; - previousCallOutput = callResult.Output.Bytes.Span.SliceWithZeroPadding(0, Math.Min(callResult.Output.Bytes.Length, (int)previousState.OutputLength)); - previousCallOutputDestination = (ulong)previousState.OutputDestination; - - - if (isTracingActions) - { - _txTracer.ReportActionRevert(previousState.GasAvailable, callResult.Output.Bytes); - } + HandleRevert(previousState, callResult, ref previousCallOutput); } } } @@ -388,42 +235,257 @@ public TransactionSubstate Run(EvmState state, IWorldState continue; Failure: + TransactionSubstate? failSubstate = HandleFailure(failure, ref previousCallOutput); + if (failSubstate is not null) { - if (_logger.IsTrace) _logger.Trace($"exception ({failure.GetType().Name}) in {currentState.ExecutionType} at depth {currentState.Env.CallDepth} - restoring snapshot"); + return failSubstate; + } + } + } - _state.Restore(currentState.Snapshot); + private void PrepareCreateData(EvmState previousState, ref ZeroPaddedSpan previousCallOutput) + { + _previousCallResult = previousState.Env.ExecutingAccount.Bytes; + _previousCallOutputDestination = UInt256.Zero; + _returnDataBuffer = Array.Empty(); + previousCallOutput = ZeroPaddedSpan.Empty; + } - RevertParityTouchBugAccount(); + private ZeroPaddedSpan HandleRegularReturn(scoped in CallResult callResult, EvmState previousState) + where TTracingInstructions : struct, IFlag + { + ZeroPaddedSpan previousCallOutput; + _returnDataBuffer = callResult.Output.Bytes; + _previousCallResult = previousState.ExecutionType.IsAnyCallEof() ? EofStatusCode.SuccessBytes : + 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)); + _previousCallOutputDestination = (ulong)previousState.OutputDestination; + if (previousState.IsPrecompile) + { + // parity induced if else for vmtrace + if (TTracingInstructions.IsActive) + { + _txTracer.ReportMemoryChange(_previousCallOutputDestination, previousCallOutput); + } + } - if (TTracingInstructions.IsActive) - { - txTracer.ReportOperationRemainingGas(0); - txTracer.ReportOperationError(failure is EvmException evmException ? evmException.ExceptionType : EvmExceptionType.Other); - } + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionEnd(previousState.GasAvailable, _returnDataBuffer); + } - if (isTracingActions) - { - EvmException evmException = failure as EvmException; - _txTracer.ReportActionError(evmException?.ExceptionType ?? EvmExceptionType.Other); - } + return previousCallOutput; + } - if (currentState.IsTopLevel) - { - return new TransactionSubstate(failure is OverflowException ? EvmExceptionType.Other : (failure as EvmException).ExceptionType, isTracing); - } + private void HandleEofCreate(in CallResult callResult, EvmState previousState, long gasAvailableForCodeDeposit, IReleaseSpec spec, ref bool previousStateSucceeded) + { + Address callCodeOwner = previousState.Env.ExecutingAccount; + // ReturnContract was called with a container index and auxdata + // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed + ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; + EofCodeInfo deployCodeInfo = (EofCodeInfo)callResult.Output.Container; + byte[] bytecodeResultArray = null; + + // 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.MachineCode.Length + auxExtraData.Length]; + // 2 - 1 - 1 - copy old container + deployCodeInfo.MachineCode.Span.CopyTo(bytecodeResult); + // 2 - 1 - 2 - copy aux data to dataSection + auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.Length..]); + + // 2 - 2 - update data section size in the header u16 + int dataSubheaderSectionStart = + EofValidator.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) + + (deployCodeInfo.EofContainer.Header.ContainerSections is null + ? 0 // container section : (0 bytes if no container section is available) + : ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) + + ONE_BYTE_LENGTH; // data section seperator + + ushort dataSize = (ushort)(deployCodeInfo.DataSection.Length + auxExtraData.Length); + bytecodeResult[dataSubheaderSectionStart + 1] = (byte)(dataSize >> 8); + bytecodeResult[dataSubheaderSectionStart + 2] = (byte)(dataSize & 0xFF); + + bytecodeResultArray = bytecodeResult.ToArray(); + + // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts + bool invalidCode = bytecodeResultArray.Length > spec.MaxCodeSize; + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, bytecodeResultArray?.Length ?? 0); + if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) + { + // 4 - set state[new_address].code to the updated deploy container + // push new_address onto the stack (already done before the ifs) + _codeInfoRepository.InsertCode(_worldState, bytecodeResultArray, callCodeOwner, spec); + _currentState.GasAvailable -= codeDepositGasCost; + + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, bytecodeResultArray); + } + } + else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) + { + _currentState.GasAvailable -= gasAvailableForCodeDeposit; + _worldState.Restore(previousState.Snapshot); + if (!previousState.IsCreateOnPreExistingAccount) + { + _worldState.DeleteAccount(callCodeOwner); + } - previousCallResult = currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; - previousCallOutputDestination = UInt256.Zero; - _returnDataBuffer = Array.Empty(); - previousCallOutput = ZeroPaddedSpan.Empty; + _previousCallResult = BytesZero; + previousStateSucceeded = false; + + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); + } + } + else if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionEnd(0L, callCodeOwner, bytecodeResultArray); + } + } + + private void HandleLegacyCreate(in CallResult callResult, EvmState previousState, long gasAvailableForCodeDeposit, IReleaseSpec spec, ref bool previousStateSucceeded) + { + Address callCodeOwner = previousState.Env.ExecutingAccount; + long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); + bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(spec, callResult.Output.Bytes); + if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) + { + ReadOnlyMemory code = callResult.Output.Bytes; + _codeInfoRepository.InsertCode(_worldState, code, callCodeOwner, spec); + _currentState.GasAvailable -= codeDepositGasCost; - currentState.Dispose(); - currentState = _stateStack.Pop(); - currentState.IsContinuation = true; + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); + } + } + else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) + { + _currentState.GasAvailable -= gasAvailableForCodeDeposit; + _worldState.Restore(previousState.Snapshot); + if (!previousState.IsCreateOnPreExistingAccount) + { + _worldState.DeleteAccount(callCodeOwner); } + + _previousCallResult = BytesZero; + previousStateSucceeded = false; + + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); + } + } + else if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); } } + private TransactionSubstate PrepareTopLevelSubstate(in CallResult callResult) + { + return new TransactionSubstate( + callResult.Output, + _currentState.Refund, + _currentState.AccessTracker.DestroyList, + (IReadOnlyCollection)_currentState.AccessTracker.Logs, + callResult.ShouldRevert, + isTracerConnected: _txTracer.IsTracing, + _logger); + } + + private void HandleRevert(EvmState previousState, in CallResult callResult, ref ZeroPaddedSpan previousCallOutput) + { + _worldState.Restore(previousState.Snapshot); + _returnDataBuffer = callResult.Output.Bytes; + _previousCallResult = previousState.ExecutionType.IsAnyCallEof() + ? (callResult.PrecompileSuccess is not null ? EofStatusCode.FailureBytes : EofStatusCode.RevertBytes) + : StatusCode.FailureBytes; + previousCallOutput = callResult.Output.Bytes.Span.SliceWithZeroPadding(0, Math.Min(callResult.Output.Bytes.Length, (int)previousState.OutputLength)); + _previousCallOutputDestination = (ulong)previousState.OutputDestination; + + if (_txTracer.IsTracingActions) + { + _txTracer.ReportActionRevert(previousState.GasAvailable, callResult.Output.Bytes); + } + } + + private TransactionSubstate? HandleFailure(Exception failure, ref ZeroPaddedSpan previousCallOutput) + where TTracingInstructions : struct, IFlag + { + if (_logger.IsTrace) _logger.Trace($"exception ({failure.GetType().Name}) in {_currentState.ExecutionType} at depth {_currentState.Env.CallDepth} - restoring snapshot"); + + _worldState.Restore(_currentState.Snapshot); + + RevertParityTouchBugAccount(); + + ITxTracer txTracer = _txTracer; + if (TTracingInstructions.IsActive) + { + txTracer.ReportOperationRemainingGas(0); + txTracer.ReportOperationError(failure is EvmException evmException ? evmException.ExceptionType : EvmExceptionType.Other); + } + + if (txTracer.IsTracingActions) + { + EvmException evmException = failure as EvmException; + txTracer.ReportActionError(evmException?.ExceptionType ?? EvmExceptionType.Other); + } + + if (_currentState.IsTopLevel) + { + return new TransactionSubstate(failure is OverflowException ? EvmExceptionType.Other : (failure as EvmException).ExceptionType, txTracer.IsTracing); + } + + _previousCallResult = _currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; + _previousCallOutputDestination = UInt256.Zero; + _returnDataBuffer = Array.Empty(); + previousCallOutput = ZeroPaddedSpan.Empty; + + _currentState.Dispose(); + _currentState = _stateStack.Pop(); + _currentState.IsContinuation = true; + return null; + } + + private void PrepareNextCallFrame(in CallResult callResult, ref ZeroPaddedSpan previousCallOutput) + { + _stateStack.Push(_currentState); + _currentState = callResult.StateToExecute; + _previousCallResult = null; + _returnDataBuffer = Array.Empty(); + previousCallOutput = ZeroPaddedSpan.Empty; + } + + private TransactionSubstate? HandleException(in CallResult callResult, ref ZeroPaddedSpan previousCallOutput) + { + if (_txTracer.IsTracingActions) _txTracer.ReportActionError(callResult.ExceptionType); + _worldState.Restore(_currentState.Snapshot); + + RevertParityTouchBugAccount(); + + if (_currentState.IsTopLevel) + { + return new TransactionSubstate(callResult.ExceptionType, _txTracer.IsTracing); + } + + _previousCallResult = _currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; + _previousCallOutputDestination = UInt256.Zero; + _returnDataBuffer = Array.Empty(); + previousCallOutput = ZeroPaddedSpan.Empty; + + _currentState.Dispose(); + _currentState = _stateStack.Pop(); + _currentState.IsContinuation = true; + return null; + } + private CallResult ExecutePrecompile(EvmState currentState, bool isTracingActions, out Exception? failure) { CallResult callResult; @@ -543,9 +605,9 @@ private void RevertParityTouchBugAccount() { if (_parityTouchBugAccount.ShouldDelete) { - if (_state.AccountExists(_parityTouchBugAccount.Address)) + if (_worldState.AccountExists(_parityTouchBugAccount.Address)) { - _state.AddToBalance(_parityTouchBugAccount.Address, UInt256.Zero, _spec); + _worldState.AddToBalance(_parityTouchBugAccount.Address, UInt256.Zero, _spec); } _parityTouchBugAccount.ShouldDelete = false; @@ -579,7 +641,7 @@ private CallResult ExecutePrecompile(EvmState state) long baseGasCost = precompile.BaseGasCost(_spec); long blobGasCost = precompile.DataGasCost(callData, _spec); - bool wasCreated = _state.AddToBalanceAndCreateIfNotExists(state.Env.ExecutingAccount, transferValue, _spec); + bool wasCreated = _worldState.AddToBalanceAndCreateIfNotExists(state.Env.ExecutingAccount, transferValue, _spec); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md // An additional issue was found in Parity, @@ -635,11 +697,11 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM ref readonly ExecutionEnvironment env = ref vmState.Env; if (!vmState.IsContinuation) { - _state.AddToBalanceAndCreateIfNotExists(env.ExecutingAccount, env.TransferValue, _spec); + _worldState.AddToBalanceAndCreateIfNotExists(env.ExecutingAccount, env.TransferValue, _spec); if (vmState.ExecutionType.IsAnyCreate() && _spec.ClearEmptyAccountWhenTouched) { - _state.IncrementNonce(env.ExecutingAccount); + _worldState.IncrementNonce(env.ExecutingAccount); } } From dfae4893e5f66d083134410b260b0e142315e979 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 03:30:27 +0000 Subject: [PATCH 234/255] Removed unneeded parameter --- .../Producers/DevBlockproducerTests.cs | 1 - src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs | 1 - .../Nethermind.Clique.Test/CliqueBlockProducerTests.cs | 4 ++-- .../Processing/OverridableTxProcessingEnv.cs | 2 +- .../Processing/ReadOnlyTxProcessingEnv.cs | 2 +- .../Nethermind.Core.Test/Blockchain/TestBlockchain.cs | 2 +- src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs | 2 +- .../Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs | 2 +- .../Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs | 2 +- src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs | 1 - .../Nethermind.Evm.Test/Tracing/GasEstimationTests.cs | 2 +- .../Nethermind.Evm.Test/TransactionProcessorEip4844Tests.cs | 2 +- .../Nethermind.Evm.Test/TransactionProcessorEip7623Tests.cs | 2 +- .../Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs | 2 +- .../Nethermind.Evm.Test/TransactionProcessorFeeTests.cs | 2 +- .../Nethermind.Evm.Test/TransactionProcessorTests.cs | 2 +- src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 -- .../Simulate/SimulateReadOnlyBlocksProcessingEnv.cs | 2 +- src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs | 1 - .../Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs | 2 +- .../Modules/Trace/ParityStyleTracerTests.cs | 2 +- .../Nethermind.Optimism/OptimismOverridableTxProcessingEnv.cs | 2 +- .../Nethermind.Optimism/OptimismReadOnlyTxProcessingEnv.cs | 2 +- .../Nethermind.Taiko.Test/TransactionProcessorTests.cs | 2 +- 25 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Producers/DevBlockproducerTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Producers/DevBlockproducerTests.cs index 1453c55dadd..a0302fec9b6 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Producers/DevBlockproducerTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Producers/DevBlockproducerTests.cs @@ -60,7 +60,6 @@ public void Test() VirtualMachine virtualMachine = new( blockhashProvider, specProvider, - codeInfoRepository, LimboLogs.Instance); TransactionProcessor txProcessor = new( specProvider, diff --git a/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs index 0f87b9ea846..845a4def1ee 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs @@ -61,7 +61,6 @@ public void Setup() VirtualMachine virtualMachine = new( blockhashProvider, specProvider, - codeInfoRepository, LimboLogs.Instance); TransactionProcessor transactionProcessor = new( specProvider, diff --git a/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs b/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs index d462ff3461b..bcaa42be817 100644 --- a/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs +++ b/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs @@ -129,7 +129,7 @@ public On CreateNode(PrivateKey privateKey, bool withGenesisAlreadyProcessed = f _genesis3Validators.Header.Hash = _genesis3Validators.Header.CalculateHash(); TransactionProcessor transactionProcessor = new(goerliSpecProvider, stateProvider, - new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, nodeLogManager), + new VirtualMachine(blockhashProvider, specProvider, nodeLogManager), codeInfoRepository, nodeLogManager); BlockProcessor blockProcessor = new( @@ -150,7 +150,7 @@ public On CreateNode(PrivateKey privateKey, bool withGenesisAlreadyProcessed = f IReadOnlyTrieStore minerTrieStore = trieStore.AsReadOnly(); WorldState minerStateProvider = new(minerTrieStore, codeDb, nodeLogManager); - VirtualMachine minerVirtualMachine = new(blockhashProvider, specProvider, codeInfoRepository, nodeLogManager); + VirtualMachine minerVirtualMachine = new(blockhashProvider, specProvider, nodeLogManager); TransactionProcessor minerTransactionProcessor = new(goerliSpecProvider, minerStateProvider, minerVirtualMachine, codeInfoRepository, nodeLogManager); BlockProcessor minerBlockProcessor = new( diff --git a/src/Nethermind/Nethermind.Consensus/Processing/OverridableTxProcessingEnv.cs b/src/Nethermind/Nethermind.Consensus/Processing/OverridableTxProcessingEnv.cs index eed81454245..a4103898122 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/OverridableTxProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/OverridableTxProcessingEnv.cs @@ -42,7 +42,7 @@ public OverridableTxProcessingEnv( StateProvider = overridableScope.WorldState; CodeInfoRepository = new(new CodeInfoRepository()); - Machine = new VirtualMachine(blockhashProvider, specProvider, CodeInfoRepository, logManager); + Machine = new VirtualMachine(blockhashProvider, specProvider, logManager); _transactionProcessorLazy = new(CreateTransactionProcessor); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/ReadOnlyTxProcessingEnv.cs b/src/Nethermind/Nethermind.Consensus/Processing/ReadOnlyTxProcessingEnv.cs index 3aef55f3647..5ff744c56ef 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/ReadOnlyTxProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/ReadOnlyTxProcessingEnv.cs @@ -71,7 +71,7 @@ ILogManager logManager BlockhashProvider = new BlockhashProvider(BlockTree, specProvider, StateProvider, logManager); CodeInfoRepository = codeInfoRepository; - Machine = new VirtualMachine(BlockhashProvider, specProvider, CodeInfoRepository, logManager); + Machine = new VirtualMachine(BlockhashProvider, specProvider, logManager); BlockTree = readOnlyBlockTree ?? throw new ArgumentNullException(nameof(readOnlyBlockTree)); BlockhashProvider = new BlockhashProvider(BlockTree, specProvider, StateProvider, logManager); diff --git a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs index 9b7b941722d..585eb086608 100644 --- a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs +++ b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs @@ -193,7 +193,7 @@ protected virtual async Task Build(ISpecProvider? specProvider = _trieStoreWatcher = new TrieStoreBoundaryWatcher(WorldStateManager, BlockTree, LogManager); ReceiptStorage = new InMemoryReceiptStorage(blockTree: BlockTree); - VirtualMachine virtualMachine = new(new BlockhashProvider(BlockTree, SpecProvider, State, LogManager), SpecProvider, codeInfoRepository, LogManager); + VirtualMachine virtualMachine = new(new BlockhashProvider(BlockTree, SpecProvider, State, LogManager), SpecProvider, LogManager); TxProcessor = new TransactionProcessor(SpecProvider, State, virtualMachine, codeInfoRepository, LogManager); BlockPreprocessorStep = new RecoverSignatures(EthereumEcdsa, TxPool, SpecProvider, LogManager); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index 6473dc1cf6f..3cc070a0377 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -44,7 +44,7 @@ public void GlobalSetup() _stateProvider.CreateAccount(Address.Zero, 1000.Ether()); _stateProvider.Commit(_spec); CodeInfoRepository codeInfoRepository = new(); - _virtualMachine = new VirtualMachine(_blockhashProvider, MainnetSpecProvider.Instance, codeInfoRepository, LimboLogs.Instance); + _virtualMachine = new VirtualMachine(_blockhashProvider, MainnetSpecProvider.Instance, LimboLogs.Instance); _environment = new ExecutionEnvironment ( diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs index f28925f6286..e2342668897 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs @@ -77,7 +77,7 @@ public void GlobalSetup() Console.WriteLine(MuirGlacier.Instance); CodeInfoRepository codeInfoRepository = new(); - _virtualMachine = new VirtualMachine(_blockhashProvider, MainnetSpecProvider.Instance, codeInfoRepository, new OneLoggerLogManager(NullLogger.Instance)); + _virtualMachine = new VirtualMachine(_blockhashProvider, MainnetSpecProvider.Instance, new OneLoggerLogManager(NullLogger.Instance)); _environment = new ExecutionEnvironment ( diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs index 9cbe402e93f..9d66492e23f 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs @@ -88,7 +88,7 @@ public void GlobalSetup() Console.WriteLine(MuirGlacier.Instance); CodeInfoRepository codeInfoRepository = new(); - _virtualMachine = new VirtualMachine(_blockhashProvider, MainnetSpecProvider.Instance, codeInfoRepository, new OneLoggerLogManager(NullLogger.Instance)); + _virtualMachine = new VirtualMachine(_blockhashProvider, MainnetSpecProvider.Instance, new OneLoggerLogManager(NullLogger.Instance)); _environment = new ExecutionEnvironment ( diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index 32c21e6d1fd..660f0c682f5 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -160,7 +160,6 @@ private static string Run(byte[] input) VirtualMachine virtualMachine = new( new TestBlockhashProvider(specProvider), specProvider, - codeInfoRepository, LimboLogs.Instance); TransactionProcessor transactionProcessor = new TransactionProcessor( specProvider, diff --git a/src/Nethermind/Nethermind.Evm.Test/Tracing/GasEstimationTests.cs b/src/Nethermind/Nethermind.Evm.Test/Tracing/GasEstimationTests.cs index db6c8c6cc3b..6471fc26978 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Tracing/GasEstimationTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Tracing/GasEstimationTests.cs @@ -363,7 +363,7 @@ public TestEnvironment() _stateProvider.CommitTree(0); CodeInfoRepository codeInfoRepository = new(); - VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, codeInfoRepository, LimboLogs.Instance); + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, LimboLogs.Instance); _transactionProcessor = new TransactionProcessor(_specProvider, _stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId); diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip4844Tests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip4844Tests.cs index febeef1a32f..c6c2814d190 100644 --- a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip4844Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip4844Tests.cs @@ -36,7 +36,7 @@ public void Setup() TrieStore trieStore = new(stateDb, LimboLogs.Instance); _stateProvider = new WorldState(trieStore, new MemDb(), LimboLogs.Instance); CodeInfoRepository codeInfoRepository = new(); - VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, codeInfoRepository, LimboLogs.Instance); + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, LimboLogs.Instance); _transactionProcessor = new TransactionProcessor(_specProvider, _stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId); } diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7623Tests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7623Tests.cs index 7834500a747..fbaf65a7880 100644 --- a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7623Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7623Tests.cs @@ -36,7 +36,7 @@ public void Setup() TrieStore trieStore = new(stateDb, LimboLogs.Instance); _stateProvider = new WorldState(trieStore, new MemDb(), LimboLogs.Instance); CodeInfoRepository codeInfoRepository = new(); - VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, codeInfoRepository, LimboLogs.Instance); + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, LimboLogs.Instance); _transactionProcessor = new TransactionProcessor(_specProvider, _stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId); } diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs index b4eecef08a0..787372d0653 100644 --- a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs @@ -39,7 +39,7 @@ public void Setup() TrieStore trieStore = new(stateDb, LimboLogs.Instance); _stateProvider = new WorldState(trieStore, new MemDb(), LimboLogs.Instance); CodeInfoRepository codeInfoRepository = new(); - VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, codeInfoRepository, LimboLogs.Instance); + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, LimboLogs.Instance); _transactionProcessor = new TransactionProcessor(_specProvider, _stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId); } diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorFeeTests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorFeeTests.cs index 79081a2f489..3c1be4306e5 100644 --- a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorFeeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorFeeTests.cs @@ -44,7 +44,7 @@ public void Setup() _stateProvider.CommitTree(0); CodeInfoRepository codeInfoRepository = new(); - VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, codeInfoRepository, LimboLogs.Instance); + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, LimboLogs.Instance); _transactionProcessor = new TransactionProcessor(_specProvider, _stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId); } diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorTests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorTests.cs index 1b43ca209e6..970e7d80a56 100644 --- a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorTests.cs @@ -59,7 +59,7 @@ public void Setup() _stateProvider.CommitTree(0); CodeInfoRepository codeInfoRepository = new(); - VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, codeInfoRepository, LimboLogs.Instance); + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, LimboLogs.Instance); _transactionProcessor = new TransactionProcessor(_specProvider, _stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId); } diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 2417f857982..24fc937265d 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -68,7 +68,7 @@ public virtual void Setup() _ethereumEcdsa = new EthereumEcdsa(SpecProvider.ChainId); IBlockhashProvider blockhashProvider = new TestBlockhashProvider(SpecProvider); CodeInfoRepository = new CodeInfoRepository(); - Machine = new VirtualMachine(blockhashProvider, SpecProvider, CodeInfoRepository, logManager); + Machine = new VirtualMachine(blockhashProvider, SpecProvider, logManager); _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 086129b4300..44ec57d882e 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -101,13 +101,11 @@ public sealed unsafe class VirtualMachine : IVirtualMachine public VirtualMachine( IBlockhashProvider? blockHashProvider, ISpecProvider? specProvider, - ICodeInfoRepository codeInfoRepository, ILogManager? logManager) { _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _blockHashProvider = blockHashProvider ?? throw new ArgumentNullException(nameof(blockHashProvider)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); } diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnv.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnv.cs index c8b7d549558..b6319aeb84b 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnv.cs @@ -71,7 +71,7 @@ public SimulateReadOnlyBlocksProcessingEnv( StateProvider = worldState; SimulateBlockhashProvider blockhashProvider = new SimulateBlockhashProvider(new BlockhashProvider(BlockTree, specProvider, StateProvider, logManager), BlockTree); CodeInfoRepository = new OverridableCodeInfoRepository(new CodeInfoRepository()); - SimulateVirtualMachine virtualMachine = new SimulateVirtualMachine(new VirtualMachine(blockhashProvider, specProvider, CodeInfoRepository, logManager)); + SimulateVirtualMachine virtualMachine = new SimulateVirtualMachine(new VirtualMachine(blockhashProvider, specProvider, logManager)); _transactionProcessor = new SimulateTransactionProcessor(SpecProvider, StateProvider, virtualMachine, CodeInfoRepository, _logManager, validate); _blockValidator = CreateValidator(); BlockTransactionPicker = new BlockProductionTransactionPicker(specProvider, true); diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index 73fac1682ef..a0e5eb96b12 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -209,7 +209,6 @@ protected VirtualMachine CreateVirtualMachine(CodeInfoRepository codeInfoReposit VirtualMachine virtualMachine = new( blockhashProvider, _api.SpecProvider, - codeInfoRepository, _api.LogManager); return virtualMachine; diff --git a/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs b/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs index 86f446d973d..70ca47a5561 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs @@ -87,7 +87,7 @@ public void GlobalSetup() LimboLogs.Instance); _blockhashProvider = new BlockhashProvider(blockTree, specProvider, stateProvider, LimboLogs.Instance); CodeInfoRepository codeInfoRepository = new(); - _virtualMachine = new VirtualMachine(_blockhashProvider, specProvider, codeInfoRepository, LimboLogs.Instance); + _virtualMachine = new VirtualMachine(_blockhashProvider, specProvider, LimboLogs.Instance); Block genesisBlock = Build.A.Block.Genesis.TestObject; blockTree.SuggestBlock(genesisBlock); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs index f4c12aeaaa6..483f71c6af6 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs @@ -62,7 +62,7 @@ public void Setup() BlockhashProvider blockhashProvider = new(_blockTree, specProvider, stateProvider, LimboLogs.Instance); CodeInfoRepository codeInfoRepository = new(); - VirtualMachine virtualMachine = new(blockhashProvider, specProvider, codeInfoRepository, LimboLogs.Instance); + VirtualMachine virtualMachine = new(blockhashProvider, specProvider, LimboLogs.Instance); TransactionProcessor transactionProcessor = new(specProvider, stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); _poSSwitcher = Substitute.For(); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismOverridableTxProcessingEnv.cs b/src/Nethermind/Nethermind.Optimism/OptimismOverridableTxProcessingEnv.cs index d6ce36e8290..1090b20a681 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismOverridableTxProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismOverridableTxProcessingEnv.cs @@ -26,7 +26,7 @@ protected override ITransactionProcessor CreateTransactionProcessor() ArgumentNullException.ThrowIfNull(LogManager); BlockhashProvider blockhashProvider = new(BlockTree, SpecProvider, StateProvider, LogManager); - VirtualMachine virtualMachine = new(blockhashProvider, SpecProvider, CodeInfoRepository, LogManager); + VirtualMachine virtualMachine = new(blockhashProvider, SpecProvider, LogManager); return new OptimismTransactionProcessor(SpecProvider, StateProvider, virtualMachine, LogManager, l1CostHelper, opSpecHelper, CodeInfoRepository); } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismReadOnlyTxProcessingEnv.cs b/src/Nethermind/Nethermind.Optimism/OptimismReadOnlyTxProcessingEnv.cs index 78d5b9a08a5..57ef73e883e 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismReadOnlyTxProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismReadOnlyTxProcessingEnv.cs @@ -30,7 +30,7 @@ protected override ITransactionProcessor CreateTransactionProcessor() ArgumentNullException.ThrowIfNull(LogManager); BlockhashProvider blockhashProvider = new(BlockTree, SpecProvider, StateProvider, LogManager); - VirtualMachine virtualMachine = new(blockhashProvider, SpecProvider, CodeInfoRepository, LogManager); + VirtualMachine virtualMachine = new(blockhashProvider, SpecProvider, LogManager); return new OptimismTransactionProcessor(SpecProvider, StateProvider, virtualMachine, LogManager, l1CostHelper, opSpecHelper, CodeInfoRepository); } } diff --git a/src/Nethermind/Nethermind.Taiko.Test/TransactionProcessorTests.cs b/src/Nethermind/Nethermind.Taiko.Test/TransactionProcessorTests.cs index ca3d02da514..bff02d6f756 100644 --- a/src/Nethermind/Nethermind.Taiko.Test/TransactionProcessorTests.cs +++ b/src/Nethermind/Nethermind.Taiko.Test/TransactionProcessorTests.cs @@ -53,7 +53,7 @@ public void Setup() _stateProvider.CommitTree(0); CodeInfoRepository codeInfoRepository = new(); - VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, codeInfoRepository, LimboLogs.Instance); + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, LimboLogs.Instance); _transactionProcessor = new TaikoTransactionProcessor(_specProvider, _stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); } From 213699924ffbe93f8c3b4582a90277c77b296a17 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 03:38:12 +0000 Subject: [PATCH 235/255] Tidy up --- .../Nethermind.Evm/VirtualMachine.cs | 77 +++++++++---------- tools/Evm/T8n/T8nExecutor.cs | 1 - 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 44ec57d882e..03a629e6f46 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -71,25 +71,10 @@ public sealed unsafe class VirtualMachine : IVirtualMachine private IWorldState _worldState = null!; private (Address Address, bool ShouldDelete) _parityTouchBugAccount = (Address.FromNumber(3), false); - private ReadOnlyMemory _returnDataBuffer = Array.Empty(); private ITxTracer _txTracer = NullTxTracer.Instance; private IReleaseSpec _spec; private ICodeInfoRepository _codeInfoRepository; - public ICodeInfoRepository CodeInfoRepository => _codeInfoRepository; - public IReleaseSpec Spec => _spec; - public ITxTracer TxTracer => _txTracer; - public IWorldState WorldState => _worldState; - public ReadOnlySpan ChainId => _chainId; - public ReadOnlyMemory ReturnDataBuffer { get => _returnDataBuffer; set => _returnDataBuffer = value; } - public object ReturnData { get => _returnData; set => _returnData = value; } - private object _returnData; - public IBlockhashProvider BlockHashProvider => _blockHashProvider; - - public EvmState EvmState => _vmState; - private EvmState _vmState; - public int SectionIndex { get => _sectionIndex; set => _sectionIndex = value; } - private int _sectionIndex; private OpCode[] _opcodeMethods; private static long _txCount; @@ -98,6 +83,18 @@ public sealed unsafe class VirtualMachine : IVirtualMachine private ReadOnlyMemory? _previousCallResult; private UInt256 _previousCallOutputDestination; + public ICodeInfoRepository CodeInfoRepository => _codeInfoRepository; + public IReleaseSpec Spec => _spec; + public ITxTracer TxTracer => _txTracer; + public IWorldState WorldState => _worldState; + public ReadOnlySpan ChainId => _chainId; + public ReadOnlyMemory ReturnDataBuffer { get; set; } = Array.Empty(); + public object ReturnData { get; private set; } + public IBlockhashProvider BlockHashProvider => _blockHashProvider; + + public EvmState EvmState { get; private set; } + public int SectionIndex { get; set; } + public VirtualMachine( IBlockhashProvider? blockHashProvider, ISpecProvider? specProvider, @@ -128,7 +125,7 @@ public TransactionSubstate Run(EvmState evmState, IWorldSt { if (!_currentState.IsContinuation) { - _returnDataBuffer = Array.Empty(); + ReturnDataBuffer = Array.Empty(); } Exception? failure; @@ -245,7 +242,7 @@ private void PrepareCreateData(EvmState previousState, ref ZeroPaddedSpan previo { _previousCallResult = previousState.Env.ExecutingAccount.Bytes; _previousCallOutputDestination = UInt256.Zero; - _returnDataBuffer = Array.Empty(); + ReturnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; } @@ -253,7 +250,7 @@ private ZeroPaddedSpan HandleRegularReturn(scoped in CallR where TTracingInstructions : struct, IFlag { ZeroPaddedSpan previousCallOutput; - _returnDataBuffer = callResult.Output.Bytes; + ReturnDataBuffer = callResult.Output.Bytes; _previousCallResult = previousState.ExecutionType.IsAnyCallEof() ? EofStatusCode.SuccessBytes : callResult.PrecompileSuccess.HasValue ? (callResult.PrecompileSuccess.Value ? StatusCode.SuccessBytes : StatusCode.FailureBytes) @@ -271,7 +268,7 @@ private ZeroPaddedSpan HandleRegularReturn(scoped in CallR if (_txTracer.IsTracingActions) { - _txTracer.ReportActionEnd(previousState.GasAvailable, _returnDataBuffer); + _txTracer.ReportActionEnd(previousState.GasAvailable, ReturnDataBuffer); } return previousCallOutput; @@ -401,7 +398,7 @@ private TransactionSubstate PrepareTopLevelSubstate(in CallResult callResult) private void HandleRevert(EvmState previousState, in CallResult callResult, ref ZeroPaddedSpan previousCallOutput) { _worldState.Restore(previousState.Snapshot); - _returnDataBuffer = callResult.Output.Bytes; + ReturnDataBuffer = callResult.Output.Bytes; _previousCallResult = previousState.ExecutionType.IsAnyCallEof() ? (callResult.PrecompileSuccess is not null ? EofStatusCode.FailureBytes : EofStatusCode.RevertBytes) : StatusCode.FailureBytes; @@ -443,7 +440,7 @@ private void HandleRevert(EvmState previousState, in CallResult callResult, ref _previousCallResult = _currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; _previousCallOutputDestination = UInt256.Zero; - _returnDataBuffer = Array.Empty(); + ReturnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; _currentState.Dispose(); @@ -457,7 +454,7 @@ private void PrepareNextCallFrame(in CallResult callResult, ref ZeroPaddedSpan p _stateStack.Push(_currentState); _currentState = callResult.StateToExecute; _previousCallResult = null; - _returnDataBuffer = Array.Empty(); + ReturnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; } @@ -475,7 +472,7 @@ private void PrepareNextCallFrame(in CallResult callResult, ref ZeroPaddedSpan p _previousCallResult = _currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; _previousCallOutputDestination = UInt256.Zero; - _returnDataBuffer = Array.Empty(); + ReturnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; _currentState.Dispose(); @@ -595,7 +592,7 @@ private void TraceTransactionActionEnd(EvmState currentState, IReleaseSpec spec, } else { - _txTracer.ReportActionEnd(currentState.GasAvailable, _returnDataBuffer); + _txTracer.ReportActionEnd(currentState.GasAvailable, ReturnDataBuffer); } } @@ -733,7 +730,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM vmState.Memory.Save(in localPreviousDest, previousCallOutput); } - _vmState = vmState; + EvmState = vmState; // Struct generic parameter is used to burn out all the if statements // and inner code by using static property on generic IFlag using // OnFlag or OffFlag. These checks that are evaluated to constant values at compile time. @@ -755,10 +752,10 @@ private unsafe CallResult ExecuteCode(scoped where TTracingInstructions : struct, IFlag where TCancelable : struct, IFlag { - _returnData = null; - _sectionIndex = _vmState.FunctionIndex; + ReturnData = null; + SectionIndex = EvmState.FunctionIndex; - ICodeInfo codeInfo = _vmState.Env.CodeInfo; + ICodeInfo codeInfo = EvmState.Env.CodeInfo; ReadOnlySpan codeSection = GetInstructions(codeInfo); EvmExceptionType exceptionType = EvmExceptionType.None; @@ -768,7 +765,7 @@ private unsafe CallResult ExecuteCode(scoped // Initialize program counter to the current state's value. // Entry point is not always 0 as we may be returning to code after a call. - int programCounter = _vmState.ProgramCounter; + int programCounter = EvmState.ProgramCounter; // Use fixed pointer or we loose the type when trying skip bounds check, // and have to cast for each call (delegate*<...> can't be used as a generic arg) fixed (OpCode* opcodeMethods = &_opcodeMethods[0]) @@ -809,7 +806,7 @@ private unsafe CallResult ExecuteCode(scoped // Exit loop if exception occurred if (exceptionType != EvmExceptionType.None) break; // Exit loop if returning data - if (_returnData is not null) break; + if (ReturnData is not null) break; if (TTracingInstructions.IsActive) EndInstructionTrace(gasAvailable); @@ -826,7 +823,7 @@ private unsafe CallResult ExecuteCode(scoped } if (exceptionType == EvmExceptionType.Revert) goto Revert; - if (_returnData is not null) goto DataReturn; + if (ReturnData is not null) goto DataReturn; return CallResult.Empty(codeInfo.Version); @@ -834,17 +831,17 @@ private unsafe CallResult ExecuteCode(scoped #if DEBUG debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); #endif - if (_returnData is EvmState state) + if (ReturnData is EvmState state) { return new CallResult(state); } - else if (_returnData is EofCodeInfo eofCodeInfo) + else if (ReturnData is EofCodeInfo eofCodeInfo) { - return new CallResult(eofCodeInfo, _returnDataBuffer, null, codeInfo.Version); + return new CallResult(eofCodeInfo, ReturnDataBuffer, null, codeInfo.Version); } - return new CallResult(null, (byte[])_returnData, null, codeInfo.Version); + return new CallResult(null, (byte[])ReturnData, null, codeInfo.Version); Revert: - return new CallResult(null, (byte[])_returnData, null, codeInfo.Version, shouldRevert: true); + return new CallResult(null, (byte[])ReturnData, null, codeInfo.Version, shouldRevert: true); OutOfGas: exceptionType = EvmExceptionType.OutOfGas; ReturnFailure: @@ -884,12 +881,12 @@ private CallResult GetFailureReturn(long gasAvailable, EvmExceptionType exceptio private void UpdateCurrentState(int pc, long gas, int stackHead) { - EvmState state = _vmState; + EvmState state = EvmState; state.ProgramCounter = pc; state.GasAvailable = gas; state.DataStackHead = stackHead; - state.FunctionIndex = _sectionIndex; + state.FunctionIndex = SectionIndex; } private static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) @@ -902,8 +899,8 @@ private static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in [MethodImpl(MethodImplOptions.NoInlining)] private void StartInstructionTrace(Instruction instruction, long gasAvailable, int programCounter, in EvmStack stackValue) { - EvmState vmState = _vmState; - int sectionIndex = _sectionIndex; + EvmState vmState = EvmState; + int sectionIndex = SectionIndex; bool isEofFrame = vmState.Env.CodeInfo.Version > 0; _txTracer.StartOperation(programCounter, instruction, gasAvailable, vmState.Env, diff --git a/tools/Evm/T8n/T8nExecutor.cs b/tools/Evm/T8n/T8nExecutor.cs index 1173e7c39c5..cb05e6fce8a 100644 --- a/tools/Evm/T8n/T8nExecutor.cs +++ b/tools/Evm/T8n/T8nExecutor.cs @@ -45,7 +45,6 @@ public static T8nExecutionResult Execute(T8nCommandArguments arguments) IVirtualMachine virtualMachine = new VirtualMachine( blockhashProvider, test.SpecProvider, - codeInfoRepository, _logManager); TransactionProcessor transactionProcessor = new( test.SpecProvider, From f2dd20a72a9644d6d38d30ba4166669392b12496 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 03:42:17 +0000 Subject: [PATCH 236/255] fix --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 03a629e6f46..e4398663909 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -89,7 +89,7 @@ public sealed unsafe class VirtualMachine : IVirtualMachine public IWorldState WorldState => _worldState; public ReadOnlySpan ChainId => _chainId; public ReadOnlyMemory ReturnDataBuffer { get; set; } = Array.Empty(); - public object ReturnData { get; private set; } + public object ReturnData { get; set; } public IBlockhashProvider BlockHashProvider => _blockHashProvider; public EvmState EvmState { get; private set; } From 8e6edadec3f7a591e268864a9cf1420ea44116d1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 03:46:07 +0000 Subject: [PATCH 237/255] fix --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index e4398663909..ea841068904 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -92,7 +92,8 @@ public sealed unsafe class VirtualMachine : IVirtualMachine public object ReturnData { get; set; } public IBlockhashProvider BlockHashProvider => _blockHashProvider; - public EvmState EvmState { get; private set; } + private EvmState _vmState; + public EvmState EvmState { get => _vmState; private set => _vmState = value; } public int SectionIndex { get; set; } public VirtualMachine( From 5283990de9a2406d0b53f7db04b6bdeae33139fa Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 16:46:39 +0000 Subject: [PATCH 238/255] Refactor --- .../Nethermind.Evm.Benchmark/EvmBenchmarks.cs | 2 +- .../MultipleUnsignedOperations.cs | 2 +- .../StaticCallBenchmarks.cs | 4 ++-- src/Nethermind/Nethermind.Evm/IVirtualMachine.cs | 2 +- .../TransactionProcessor.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 16 ++++++++-------- .../Simulate/SimulateVirtualMachine.cs | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index 3cc070a0377..f0681e4e577 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -64,7 +64,7 @@ public void GlobalSetup() [Benchmark] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.ExecuteTransaction(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } } diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs index e2342668897..fc0721aeb92 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs @@ -97,7 +97,7 @@ public void GlobalSetup() [Benchmark] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.ExecuteTransaction(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs index 9d66492e23f..5b4500a00ba 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs @@ -108,14 +108,14 @@ public void GlobalSetup() [Benchmark(Baseline = true)] public void ExecuteCode() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.ExecuteTransaction(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } [Benchmark] public void ExecuteCodeNoTracing() { - _virtualMachine.Run(_evmState, _stateProvider, _txTracer); + _virtualMachine.ExecuteTransaction(_evmState, _stateProvider, _txTracer); _stateProvider.Reset(); } diff --git a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs index 6d2dac54688..ba86df4da76 100644 --- a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs @@ -12,7 +12,7 @@ namespace Nethermind.Evm { public interface IVirtualMachine { - TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) + TransactionSubstate ExecuteTransaction(EvmState state, IWorldState worldState, ITxTracer txTracer) where TTracingInstructions : struct, IFlag; } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 0796981c886..1f5b371aa40 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -658,7 +658,7 @@ protected virtual void ExecuteEvmCall( using (EvmState state = EvmState.RentTopLevel(unspentGas, executionType, snapshot, env, accessedItems)) { - substate = VirtualMachine.Run(state, WorldState, tracer); + substate = VirtualMachine.ExecuteTransaction(state, WorldState, tracer); unspentGas = state.GasAvailable; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index ea841068904..3321b804246 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -107,7 +107,7 @@ public VirtualMachine( _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); } - public TransactionSubstate Run(EvmState evmState, IWorldState worldState, ITxTracer txTracer) + public TransactionSubstate ExecuteTransaction(EvmState evmState, IWorldState worldState, ITxTracer txTracer) where TTracingInstructions : struct, IFlag { _txTracer = txTracer; @@ -490,7 +490,7 @@ private CallResult ExecutePrecompile(EvmState currentState, bool isTracingAction _txTracer.ReportAction(currentState.GasAvailable, currentState.Env.Value, currentState.From, currentState.To, currentState.Env.InputData, currentState.ExecutionType, true); } - callResult = ExecutePrecompile(currentState); + callResult = RunPrecompile(currentState); if (!callResult.PrecompileSuccess.Value) { @@ -627,7 +627,7 @@ private enum StorageAccessType SSTORE } - private CallResult ExecutePrecompile(EvmState state) + private CallResult RunPrecompile(EvmState state) { ReadOnlyMemory callData = state.Env.InputData; UInt256 transferValue = state.Env.TransferValue; @@ -665,7 +665,7 @@ private CallResult ExecutePrecompile(EvmState state) try { (ReadOnlyMemory output, bool success) = precompile.Run(callData, _spec); - CallResult callResult = new(output, success, 0, !success); + CallResult callResult = new(output, precompileSuccess: success, fromVersion: 0, shouldRevert: !success); return callResult; } catch (DllNotFoundException exception) @@ -676,7 +676,7 @@ private CallResult ExecutePrecompile(EvmState state) catch (Exception exception) { if (_logger.IsError) _logger.Error($"Precompiled contract ({precompile.GetType()}) execution exception", exception); - CallResult callResult = new(default, false, 0, true); + CallResult callResult = new(output: default, precompileSuccess: false, fromVersion: 0, shouldRevert: true); return callResult; } } @@ -739,8 +739,8 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM // which use shared generics. return _txTracer.IsCancelable switch { - false => ExecuteCode(ref stack, gasAvailable), - true => ExecuteCode(ref stack, gasAvailable), + false => RunByteCode(ref stack, gasAvailable), + true => RunByteCode(ref stack, gasAvailable), }; Empty: return CallResult.Empty(vmState.Env.CodeInfo.Version); @@ -749,7 +749,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM } [SkipLocalsInit] - private unsafe CallResult ExecuteCode(scoped ref EvmStack stack, long gasAvailable) + private unsafe CallResult RunByteCode(scoped ref EvmStack stack, long gasAvailable) where TTracingInstructions : struct, IFlag where TCancelable : struct, IFlag { diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs index 9e79baff0ef..39094876015 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateVirtualMachine.cs @@ -10,7 +10,7 @@ namespace Nethermind.Facade.Simulate; public class SimulateVirtualMachine(IVirtualMachine virtualMachine) : IVirtualMachine { - public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) + public TransactionSubstate ExecuteTransaction(EvmState state, IWorldState worldState, ITxTracer txTracer) where TTracingInstructions : struct, IFlag { if (txTracer.IsTracingActions && TryGetLogsMutator(txTracer, out ITxLogsMutator logsMutator)) @@ -18,7 +18,7 @@ public TransactionSubstate Run(EvmState state, IWorldState logsMutator.SetLogsToMutate(state.AccessTracker.Logs); } - return virtualMachine.Run(state, worldState, txTracer); + return virtualMachine.ExecuteTransaction(state, worldState, txTracer); } private static bool TryGetLogsMutator(ITxTracer txTracer, [NotNullWhen(true)] out ITxLogsMutator? txLogsMutator) From 3b264bfdc601e064b720861ec9f37357663d17ff Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 17:26:29 +0000 Subject: [PATCH 239/255] Add comments --- .../Nethermind.Evm/VirtualMachine.cs | 508 +++++++++++++++--- 1 file changed, 441 insertions(+), 67 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3321b804246..63aacedb36e 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -32,7 +32,10 @@ namespace Nethermind.Evm; using unsafe OpCode = delegate*; using Int256; -public sealed unsafe class VirtualMachine : IVirtualMachine +public sealed unsafe class VirtualMachine( + IBlockhashProvider? blockHashProvider, + ISpecProvider? specProvider, + ILogManager? logManager) : IVirtualMachine { public const int MaxCallDepth = Eof1.RETURN_STACK_MAX_HEIGHT; private readonly static UInt256 P255Int = (UInt256)System.Numerics.BigInteger.Pow(2, 255); @@ -62,11 +65,11 @@ public sealed unsafe class VirtualMachine : IVirtualMachine internal static readonly PrecompileExecutionFailureException PrecompileExecutionFailureException = new(); internal static readonly OutOfGasException PrecompileOutOfGasException = new(); - private readonly byte[] _chainId; + private readonly byte[] _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); - private readonly IBlockhashProvider _blockHashProvider; - private readonly ISpecProvider _specProvider; - private readonly ILogger _logger; + private readonly IBlockhashProvider _blockHashProvider = blockHashProvider ?? throw new ArgumentNullException(nameof(blockHashProvider)); + private readonly ISpecProvider _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); + private readonly ILogger _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); private readonly Stack _stateStack = new(); private IWorldState _worldState = null!; @@ -96,34 +99,51 @@ public sealed unsafe class VirtualMachine : IVirtualMachine public EvmState EvmState { get => _vmState; private set => _vmState = value; } public int SectionIndex { get; set; } - public VirtualMachine( - IBlockhashProvider? blockHashProvider, - ISpecProvider? specProvider, - ILogManager? logManager) - { - _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - _blockHashProvider = blockHashProvider ?? throw new ArgumentNullException(nameof(blockHashProvider)); - _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); - } - - public TransactionSubstate ExecuteTransaction(EvmState evmState, IWorldState worldState, ITxTracer txTracer) + /// + /// Executes a transaction by iteratively processing call frames until a top-level call returns + /// or a failure condition is reached. This method handles both precompiled contracts and regular + /// EVM calls, along with proper state management, tracing, and error handling. + /// + /// + /// The type of tracing instructions flag used to conditionally trace execution actions. + /// + /// The initial EVM state to begin transaction execution. + /// The current world state that may be modified during execution. + /// An object used to record execution details and trace transaction actions. + /// + /// A representing the final state of the transaction execution. + /// + /// + /// Thrown when an EVM-specific error occurs during execution. + /// + public TransactionSubstate ExecuteTransaction( + EvmState evmState, + IWorldState worldState, + ITxTracer txTracer) where TTracingInstructions : struct, IFlag { + // Initialize dependencies for transaction tracing and state access. _txTracer = txTracer; _worldState = worldState; + // Extract the transaction execution context from the EVM environment. ref readonly TxExecutionContext txExecutionContext = ref evmState.Env.TxExecutionContext; - IReleaseSpec spec = PrepareSpecAndOpcodes(txExecutionContext.BlockExecutionContext.Header); + // Prepare the specification and opcode mapping based on the current block header. + IReleaseSpec spec = PrepareSpecAndOpcodes( + txExecutionContext.BlockExecutionContext.Header); + + // Initialize the code repository and set up the initial execution state. _codeInfoRepository = txExecutionContext.CodeInfoRepository; _currentState = evmState; _previousCallResult = null; _previousCallOutputDestination = UInt256.Zero; ZeroPaddedSpan previousCallOutput = ZeroPaddedSpan.Empty; + // Main execution loop: processes call frames until the top-level transaction completes. while (true) { + // For non-continuation frames, clear any previously stored return data. if (!_currentState.IsContinuation) { ReturnDataBuffer = Array.Empty(); @@ -133,36 +153,47 @@ public TransactionSubstate ExecuteTransaction(EvmState evm try { CallResult callResult; + + // If the current state represents a precompiled contract, handle it separately. if (_currentState.IsPrecompile) { callResult = ExecutePrecompile(_currentState, _txTracer.IsTracingActions, out failure); if (failure is not null) { + // Jump to the failure handler if a precompile error occurred. goto Failure; } } else { + // Start transaction tracing for non-continuation frames if tracing is enabled. if (_txTracer.IsTracingActions && !_currentState.IsContinuation) { TraceTransactionActionStart(_currentState); } + // Execute the regular EVM call if valid code is present; otherwise, mark as invalid. if (_currentState.Env.CodeInfo is not null) { - callResult = ExecuteCall(_currentState, _previousCallResult, previousCallOutput, _previousCallOutputDestination); + callResult = ExecuteCall( + _currentState, + _previousCallResult, + previousCallOutput, + _previousCallOutputDestination); } else { callResult = CallResult.InvalidCodeException; } + // If the call did not finish with a return, set up the next call frame and continue. if (!callResult.IsReturn) { PrepareNextCallFrame(in callResult, ref previousCallOutput); continue; } + // Handle exceptions raised during the call execution. if (callResult.IsException) { TransactionSubstate? substate = HandleException(in callResult, ref previousCallOutput); @@ -170,47 +201,65 @@ public TransactionSubstate ExecuteTransaction(EvmState evm { return substate; } + // Continue execution if the exception did not immediately finalize the transaction. continue; } } + // If the current execution state is the top-level call, finalize tracing and return the result. if (_currentState.IsTopLevel) { if (_txTracer.IsTracingActions) { TraceTransactionActionEnd(_currentState, spec, callResult); } - return PrepareTopLevelSubstate(in callResult); } + // For nested call frames, merge the results and restore the previous execution state. using (EvmState previousState = _currentState) { + // Restore the previous state from the stack and mark it as a continuation. _currentState = _stateStack.Pop(); _currentState.IsContinuation = true; + // Refund the remaining gas from the completed call frame. _currentState.GasAvailable += previousState.GasAvailable; bool previousStateSucceeded = true; if (!callResult.ShouldRevert) { long gasAvailableForCodeDeposit = previousState.GasAvailable; + + // Process contract creation calls differently from regular calls. if (previousState.ExecutionType.IsAnyCreate()) { PrepareCreateData(previousState, ref previousCallOutput); if (previousState.ExecutionType.IsAnyCreateLegacy()) { - HandleLegacyCreate(in callResult, previousState, gasAvailableForCodeDeposit, spec, ref previousStateSucceeded); + HandleLegacyCreate( + in callResult, + previousState, + gasAvailableForCodeDeposit, + spec, + ref previousStateSucceeded); } else if (previousState.ExecutionType.IsAnyCreateEof()) { - HandleEofCreate(in callResult, previousState, gasAvailableForCodeDeposit, spec, ref previousStateSucceeded); + HandleEofCreate( + in callResult, + previousState, + gasAvailableForCodeDeposit, + spec, + ref previousStateSucceeded); } } else { + // Process a standard call return. previousCallOutput = HandleRegularReturn(in callResult, previousState); } + // Commit the changes from the completed call frame if execution was successful. if (previousStateSucceeded) { previousState.CommitToParent(_currentState); @@ -218,18 +267,22 @@ public TransactionSubstate ExecuteTransaction(EvmState evm } else { + // Revert state changes for the previous call frame when a revert condition is signaled. HandleRevert(previousState, callResult, ref previousCallOutput); } } } + // Handle specific EVM or overflow exceptions by routing to the failure handling block. catch (Exception ex) when (ex is EvmException or OverflowException) { failure = ex; goto Failure; } + // Continue with the next iteration of the execution loop. continue; + // Failure handling: attempts to process and possibly finalize the transaction after an error. Failure: TransactionSubstate? failSubstate = HandleFailure(failure, ref previousCallOutput); if (failSubstate is not null) @@ -345,40 +398,92 @@ private void HandleEofCreate(in CallResult callResult, EvmState previousState, l } } - private void HandleLegacyCreate(in CallResult callResult, EvmState previousState, long gasAvailableForCodeDeposit, IReleaseSpec spec, ref bool previousStateSucceeded) + /// + /// Handles the code deposit for a legacy contract creation operation. + /// This method calculates the gas cost for depositing the contract code using legacy rules, + /// validates the code, and either deposits the code or reverts the world state if the deposit fails. + /// + /// + /// The result of the contract creation call, which includes the output code intended for deposit. + /// + /// + /// The EVM state prior to the current call frame. It provides access to the snapshot, the executing account, + /// and flags indicating if the account pre-existed. + /// + /// + /// The amount of gas available for covering the cost of code deposit. + /// + /// + /// The release specification containing the rules and parameters that affect code deposit behavior. + /// + /// + /// A reference flag indicating whether the previous call frame executed successfully. This flag is set to false if the deposit fails. + /// + private void HandleLegacyCreate( + in CallResult callResult, + EvmState previousState, + long gasAvailableForCodeDeposit, + IReleaseSpec spec, + ref bool previousStateSucceeded) { + // Cache whether transaction tracing is enabled to avoid multiple property lookups. + bool isTracing = _txTracer.IsTracingActions; + + // Get the address of the account that initiated the contract creation. Address callCodeOwner = previousState.Env.ExecutingAccount; + + // Calculate the gas cost required to deposit the contract code using legacy cost rules. long codeDepositGasCost = CodeDepositHandler.CalculateCost(spec, callResult.Output.Bytes.Length); + + // Validate the code against legacy rules; mark it invalid if it fails these checks. bool invalidCode = !CodeDepositHandler.IsValidWithLegacyRules(spec, callResult.Output.Bytes); + + // Check if there is sufficient gas and the code is valid. if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { + // Deposit the contract code into the repository. ReadOnlyMemory code = callResult.Output.Bytes; _codeInfoRepository.InsertCode(_worldState, code, callCodeOwner, spec); + + // Deduct the gas cost for the code deposit from the current state's available gas. _currentState.GasAvailable -= codeDepositGasCost; - if (_txTracer.IsTracingActions) + // If tracing is enabled, report the successful code deposit operation. + if (isTracing) { _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); } } + // If the code deposit should fail due to out-of-gas or invalid code conditions... else if (spec.FailOnOutOfGasCodeDeposit || invalidCode) { + // Consume all remaining gas allocated for the code deposit. _currentState.GasAvailable -= gasAvailableForCodeDeposit; + + // Roll back the world state to its snapshot from before the creation attempt. _worldState.Restore(previousState.Snapshot); + + // If the contract creation did not target a pre-existing account, delete the account. if (!previousState.IsCreateOnPreExistingAccount) { _worldState.DeleteAccount(callCodeOwner); } + // Reset the previous call result to indicate that no valid code was deployed. _previousCallResult = BytesZero; + + // Mark the previous state execution as failed. previousStateSucceeded = false; - if (_txTracer.IsTracingActions) + // Report an error via the tracer, indicating whether the failure was due to invalid code or gas exhaustion. + if (isTracing) { _txTracer.ReportActionError(invalidCode ? EvmExceptionType.InvalidCode : EvmExceptionType.OutOfGas); } } - else if (_txTracer.IsTracingActions) + // In scenarios where the code deposit does not strictly mandate a failure, + // report the end of the action if tracing is enabled. + else if (isTracing) { _txTracer.ReportActionEnd(previousState.GasAvailable - codeDepositGasCost, callCodeOwner, callResult.Output.Bytes); } @@ -396,126 +501,285 @@ private TransactionSubstate PrepareTopLevelSubstate(in CallResult callResult) _logger); } + /// + /// Reverts the state changes made during the execution of a call frame. + /// This method restores the world state to a previous snapshot, sets appropriate + /// failure indicators and output data, and reports the revert action via the tracer. + /// + /// + /// The EVM state prior to the current call, which contains the snapshot for restoration, + /// output length, output destination, and execution type. + /// + /// + /// The result of the call that triggered the revert, containing output data and flags + /// indicating precompile success. + /// + /// + /// A reference to the output data buffer that will be updated with the reverted call's output, + /// padded to match the expected length. + /// private void HandleRevert(EvmState previousState, in CallResult callResult, ref ZeroPaddedSpan previousCallOutput) { + // Restore the world state to the snapshot taken before the execution of the call. _worldState.Restore(previousState.Snapshot); - ReturnDataBuffer = callResult.Output.Bytes; + + // Cache the output bytes from the call result to avoid multiple property accesses. + ReadOnlyMemory outputBytes = callResult.Output.Bytes; + + // 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) + ? (callResult.PrecompileSuccess is not null + ? EofStatusCode.FailureBytes + : EofStatusCode.RevertBytes) : StatusCode.FailureBytes; - previousCallOutput = callResult.Output.Bytes.Span.SliceWithZeroPadding(0, Math.Min(callResult.Output.Bytes.Length, (int)previousState.OutputLength)); + + // 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. + previousCallOutput = outputBytes.Span.SliceWithZeroPadding(0, Math.Min(outputBytes.Length, (int)previousState.OutputLength)); + + // Record the output destination address for subsequent operations. _previousCallOutputDestination = (ulong)previousState.OutputDestination; + // If transaction tracing is enabled, report the revert action along with the available gas and output bytes. if (_txTracer.IsTracingActions) { - _txTracer.ReportActionRevert(previousState.GasAvailable, callResult.Output.Bytes); + _txTracer.ReportActionRevert(previousState.GasAvailable, outputBytes); } } + /// + /// Handles a failure during transaction execution by restoring the world state, + /// reporting error details via the tracer, and either finalizing the top-level transaction + /// or preparing to revert to the parent call frame. + /// + /// + /// A type parameter representing tracing instructions. It must be a struct implementing . + /// + /// The exception that caused the failure during execution. + /// + /// A reference to the zero-padded span that holds the previous call's output; it will be reset upon failure. + /// + /// + /// A if the failure occurs in the top-level call; otherwise, null + /// to indicate that execution should continue with the parent call frame. + /// private TransactionSubstate? HandleFailure(Exception failure, ref ZeroPaddedSpan previousCallOutput) where TTracingInstructions : struct, IFlag { - if (_logger.IsTrace) _logger.Trace($"exception ({failure.GetType().Name}) in {_currentState.ExecutionType} at depth {_currentState.Env.CallDepth} - restoring snapshot"); + // Log the exception if trace logging is enabled. + if (_logger.IsTrace) + { + _logger.Trace($"exception ({failure.GetType().Name}) in {_currentState.ExecutionType} at depth {_currentState.Env.CallDepth} - restoring snapshot"); + } + // Revert the world state to the snapshot taken at the start of the current state's execution. _worldState.Restore(_currentState.Snapshot); + // Revert any modifications specific to the Parity touch bug, if applicable. RevertParityTouchBugAccount(); + // Cache the transaction tracer for local use. ITxTracer txTracer = _txTracer; + + // Attempt to cast the exception to EvmException to extract a specific error type. + EvmException? evmException = failure as EvmException; + EvmExceptionType errorType = evmException?.ExceptionType ?? EvmExceptionType.Other; + + // If the tracing instructions flag is active, report zero remaining gas and log the error. if (TTracingInstructions.IsActive) { txTracer.ReportOperationRemainingGas(0); - txTracer.ReportOperationError(failure is EvmException evmException ? evmException.ExceptionType : EvmExceptionType.Other); + txTracer.ReportOperationError(errorType); } + // If action-level tracing is enabled, report the error associated with the action. if (txTracer.IsTracingActions) { - EvmException evmException = failure as EvmException; - txTracer.ReportActionError(evmException?.ExceptionType ?? EvmExceptionType.Other); + txTracer.ReportActionError(errorType); } + // For a top-level call, immediately return a final transaction substate. if (_currentState.IsTopLevel) { - return new TransactionSubstate(failure is OverflowException ? EvmExceptionType.Other : (failure as EvmException).ExceptionType, txTracer.IsTracing); + // For an OverflowException, force the error type to a generic Other error. + EvmExceptionType finalErrorType = failure is OverflowException ? EvmExceptionType.Other : errorType; + return new TransactionSubstate(finalErrorType, txTracer.IsTracing); } - _previousCallResult = _currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; + // 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; + + // Reset output destination and return data. _previousCallOutputDestination = UInt256.Zero; ReturnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; + // Dispose of the current failing state and restore the previous call frame from the stack. _currentState.Dispose(); _currentState = _stateStack.Pop(); _currentState.IsContinuation = true; + return null; } + /// + /// Prepares the execution environment for the next call frame by updating the current state + /// and resetting relevant output fields. + /// + /// + /// The result object from the current call, which contains the state to be executed next. + /// + /// + /// A reference to the buffer holding the previous call's output, which is cleared in preparation for the new call. + /// private void PrepareNextCallFrame(in CallResult callResult, ref ZeroPaddedSpan previousCallOutput) { + // Push the current execution state onto the state stack so it can be restored later. _stateStack.Push(_currentState); + + // Transition to the next call frame's state provided by the call result. _currentState = callResult.StateToExecute; + + // Clear the previous call result as the execution context is moving to a new frame. _previousCallResult = null; + + // Reset the return data buffer to ensure no residual data persists across call frames. ReturnDataBuffer = Array.Empty(); + + // Clear the previous call output, preparing for new output data in the next call frame. previousCallOutput = ZeroPaddedSpan.Empty; } + /// + /// Handles exceptions that occur during the execution of a call frame by restoring the world state, + /// reverting known side effects, and either finalizing the transaction (for top-level calls) or + /// preparing to resume execution in a parent call frame (for nested calls). + /// + /// + /// The result object that contains the exception type and any output data from the failed call. + /// + /// + /// A reference to the zero-padded span that holds the previous call's output, which is reset on exception. + /// + /// + /// A instance if the failure occurred in a top-level call, + /// otherwise null to indicate that execution should continue in the parent frame. + /// private TransactionSubstate? HandleException(in CallResult callResult, ref ZeroPaddedSpan previousCallOutput) { - if (_txTracer.IsTracingActions) _txTracer.ReportActionError(callResult.ExceptionType); + // Cache the tracer to minimize repeated field accesses. + ITxTracer txTracer = _txTracer; + + // Report the error for action-level tracing if enabled. + if (txTracer.IsTracingActions) + { + txTracer.ReportActionError(callResult.ExceptionType); + } + + // Restore the world state to its snapshot before the current call execution. _worldState.Restore(_currentState.Snapshot); + // Revert any modifications that might have been applied due to the Parity touch bug. RevertParityTouchBugAccount(); + // If this is the top-level call, return a final transaction substate encapsulating the error. if (_currentState.IsTopLevel) { - return new TransactionSubstate(callResult.ExceptionType, _txTracer.IsTracing); + return new TransactionSubstate(callResult.ExceptionType, txTracer.IsTracing); } - _previousCallResult = _currentState.ExecutionType.IsAnyCallEof() ? EofStatusCode.FailureBytes : StatusCode.FailureBytes; + // 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; + + // Reset output destination and clear return data. _previousCallOutputDestination = UInt256.Zero; ReturnDataBuffer = Array.Empty(); previousCallOutput = ZeroPaddedSpan.Empty; + // Clean up the current failing state and pop the parent call frame from the state stack. _currentState.Dispose(); _currentState = _stateStack.Pop(); _currentState.IsContinuation = true; + + // Return null to indicate that the failure was handled and execution should continue in the parent frame. return null; } + /// + /// Executes a precompiled contract operation based on the current execution state. + /// If tracing is enabled, reports the precompile action. It then runs the precompile operation, + /// checks for failure conditions, and adjusts the execution state accordingly. + /// + /// The current EVM state containing execution parameters for the precompile. + /// + /// A boolean indicating whether detailed tracing actions should be reported during execution. + /// + /// + /// An output parameter that is set to the encountered exception if the precompile fails; otherwise, null. + /// + /// + /// A containing the results of the precompile execution. In case of a failure, + /// returns the default value of . + /// private CallResult ExecutePrecompile(EvmState currentState, bool isTracingActions, out Exception? failure) { - CallResult callResult; + // Report the precompile action if tracing is enabled. if (isTracingActions) { - _txTracer.ReportAction(currentState.GasAvailable, currentState.Env.Value, currentState.From, currentState.To, currentState.Env.InputData, currentState.ExecutionType, true); + _txTracer.ReportAction( + currentState.GasAvailable, + currentState.Env.Value, + currentState.From, + currentState.To, + currentState.Env.InputData, + currentState.ExecutionType, + true); } - callResult = RunPrecompile(currentState); + // Execute the precompile operation with the current state. + CallResult callResult = RunPrecompile(currentState); + // If the precompile did not succeed, handle the failure conditions. if (!callResult.PrecompileSuccess.Value) { + // If the failure is due to an exception (e.g., out-of-gas), set the corresponding failure exception. if (callResult.IsException) { failure = VirtualMachine.PrecompileOutOfGasException; goto Failure; } + + // If running a precompile on a top-level call frame and it fails, assign a general execution failure. if (currentState.IsPrecompile && currentState.IsTopLevel) { failure = VirtualMachine.PrecompileExecutionFailureException; - // TODO: when direct / calls are treated same we should not need such differentiation goto Failure; } - // TODO: testing it as it seems the way to pass zkSNARKs tests + // Otherwise, if no exception but precompile did not succeed, exhaust the remaining gas. currentState.GasAvailable = 0; } + // If execution reaches here, the precompile operation is considered successful. failure = null; return callResult; + Failure: + // Return the default CallResult to signal failure, with the failure exception set via the out parameter. return default; } + private void TraceTransactionActionStart(EvmState currentState) { _txTracer.ReportAction(currentState.GasAvailable, @@ -530,67 +794,119 @@ private void TraceTransactionActionStart(EvmState currentState) if (_txTracer.IsTracingCode) _txTracer.ReportByteCode(currentState.Env.CodeInfo?.MachineCode ?? default); } - private IReleaseSpec PrepareSpecAndOpcodes(BlockHeader header) where TTracingInstructions : struct, IFlag + /// + /// Prepares the release specification and opcode methods to be used during EVM execution, + /// based on the provided block header and the tracing instructions flag. + /// + /// + /// A value type implementing that indicates whether tracing-specific opcodes + /// should be used. + /// + /// + /// The block header containing the block number and timestamp, which are used to select the appropriate release specification. + /// + /// + /// The prepared instance, with its associated opcode methods cached for execution. + /// + private IReleaseSpec PrepareSpecAndOpcodes(BlockHeader header) + where TTracingInstructions : struct, IFlag { + // Retrieve the release specification based on the block's number and timestamp. IReleaseSpec spec = _specProvider.GetSpec(header.Number, header.Timestamp); + + // Check if tracing instructions are inactive. if (!TTracingInstructions.IsActive) { + // Occasionally refresh the opcode cache for non-tracing opcodes. + // The cache is flushed every 10,000 transactions until a threshold of 500,000 transactions. + // This is to have the function pointers directly point at any PGO optimized methods rather than via pre-stubs + // May be a few cycles to pick up pointers to the re-Jitted optimized methods depending on what's in the blocks, + // however the the refreshes don't take long. (re-Jitting doesn't update prior captured function pointers) if (_txCount < 500_000 && Interlocked.Increment(ref _txCount) % 10_000 == 0) { - if (_logger.IsDebug) _logger.Debug("Resetting EVM instructions cache"); - // Flush the cache every 10_000 transactions to directly point at any PGO optimized methods rather than via pre-stubs - // May be a few cycles to pick up pointers to the optimized methods depending on what's in the blocks, - // however the the refreshes don't take long. + if (_logger.IsDebug) + { + _logger.Debug("Resetting EVM instruction cache"); + } + // Regenerate the non-traced opcode set to pick up any updated PGO optimized methods. spec.EvmInstructions = EvmInstructions.GenerateOpCodes(spec); } + // Ensure the non-traced opcode set is generated and assign it to the _opcodeMethods field. _opcodeMethods = (OpCode[])(spec.EvmInstructions ??= EvmInstructions.GenerateOpCodes(spec)); } else { + // For tracing-enabled execution, generate (if necessary) and cache the traced opcode set. _opcodeMethods = (OpCode[])(spec.EvmTracedInstructions ??= EvmInstructions.GenerateOpCodes(spec)); } + // Store the spec in field for future access and return it. return (_spec = spec); } + /// + /// Reports the final outcome of a transaction action to the transaction tracer, taking into account + /// various conditions such as exceptions, reverts, and contract creation flows. For contract creation, + /// the method adjusts the available gas by the code deposit cost and validates the deployed code. + /// + /// + /// The current EVM state, which contains the available gas, execution type, and target address. + /// + /// + /// The release specification that provides rules and parameters such as code deposit cost and code validation. + /// + /// + /// The result of the executed call, including output bytes, exception and revert flags, and additional metadata. + /// private void TraceTransactionActionEnd(EvmState currentState, IReleaseSpec spec, in CallResult callResult) { + // 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); + // Cache the output bytes for reuse in the tracing reports. + ReadOnlyMemory outputBytes = callResult.Output.Bytes; + + // If an exception occurred during execution, report the error immediately. if (callResult.IsException) { _txTracer.ReportActionError(callResult.ExceptionType); } + // If the call is set to revert, report a revert action, adjusting the reported gas for creation operations. else if (callResult.ShouldRevert) { - _txTracer.ReportActionRevert(currentState.ExecutionType.IsAnyCreate() - ? currentState.GasAvailable - codeDepositGasCost - : currentState.GasAvailable, - callResult.Output.Bytes); + // For creation operations, subtract the code deposit cost from the available gas; otherwise, use full gas. + long reportedGas = currentState.ExecutionType.IsAnyCreate() ? currentState.GasAvailable - codeDepositGasCost : currentState.GasAvailable; + _txTracer.ReportActionRevert(reportedGas, outputBytes); } + // Process contract creation flows. else if (currentState.ExecutionType.IsAnyCreate()) { + // If available gas is insufficient to cover the code deposit cost... if (currentState.GasAvailable < codeDepositGasCost) { + // When the spec mandates charging for top-level creation, report an out-of-gas error. if (spec.ChargeForTopLevelCreate) { _txTracer.ReportActionError(EvmExceptionType.OutOfGas); } + // Otherwise, report a successful action end with the remaining gas. else { - _txTracer.ReportActionEnd(currentState.GasAvailable, currentState.To, callResult.Output.Bytes); + _txTracer.ReportActionEnd(currentState.GasAvailable, currentState.To, outputBytes); } } - // Reject code starting with 0xEF if EIP-3541 is enabled. - else if (CodeDepositHandler.CodeIsInvalid(spec, callResult.Output.Bytes, callResult.FromVersion)) + // 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)) { _txTracer.ReportActionError(EvmExceptionType.InvalidCode); } + // In the successful contract creation case, deduct the code deposit gas cost and report a normal action end. else { - _txTracer.ReportActionEnd(currentState.GasAvailable - codeDepositGasCost, currentState.To, callResult.Output.Bytes); + _txTracer.ReportActionEnd(currentState.GasAvailable - codeDepositGasCost, currentState.To, outputBytes); } } + // For non-creation calls, report the action end using the current available gas and the standard return data. else { _txTracer.ReportActionEnd(currentState.GasAvailable, ReturnDataBuffer); @@ -681,28 +997,64 @@ private CallResult RunPrecompile(EvmState state) } } + /// + /// Executes an EVM call by preparing the execution environment, including account balance adjustments, + /// stack initialization, and memory updates. It then dispatches the bytecode execution using a + /// specialized interpreter that is optimized at compile time based on the tracing instructions flag. + /// + /// + /// A struct implementing that indicates, via a compile-time constant, + /// whether tracing-specific opcodes and behavior should be used. + /// + /// + /// The current EVM state containing the execution environment, gas, memory, and stack information. + /// + /// + /// An optional read-only memory buffer containing the output of a previous call; if provided, its bytes + /// will be pushed onto the stack for further processing. + /// + /// + /// A zero-padded span containing output from the previous call used for updating the memory state. + /// + /// + /// The memory destination address where the previous call's output should be stored. + /// + /// + /// A that encapsulates the result of executing the EVM call, including success, + /// failure, or out-of-gas conditions. + /// /// - /// Struct generic parameter is used to burn out all the if statements and inner code - /// by typeof(TTracingInstructions) == typeof(NotTracing) checks that are evaluated to constant - /// values at compile time. + /// The generic struct parameter is used to eliminate runtime if-statements via compile-time evaluation + /// of TTracingInstructions.IsActive. /// [SkipLocalsInit] - private CallResult ExecuteCall(EvmState vmState, ReadOnlyMemory? previousCallResult, ZeroPaddedSpan previousCallOutput, scoped in UInt256 previousCallOutputDestination) + private CallResult ExecuteCall( + EvmState vmState, + ReadOnlyMemory? previousCallResult, + ZeroPaddedSpan previousCallOutput, + scoped in UInt256 previousCallOutputDestination) where TTracingInstructions : struct, IFlag { + // Obtain a reference to the execution environment for convenience. ref readonly ExecutionEnvironment env = ref vmState.Env; + + // If this is the first call frame (not a continuation), adjust account balances and nonces. if (!vmState.IsContinuation) { + // Ensure the executing account has sufficient balance and exists in the world state. _worldState.AddToBalanceAndCreateIfNotExists(env.ExecutingAccount, env.TransferValue, _spec); + // For contract creation calls, increment the nonce if the specification requires it. if (vmState.ExecutionType.IsAnyCreate() && _spec.ClearEmptyAccountWhenTouched) { _worldState.IncrementNonce(env.ExecutingAccount); } } + // If no machine code is present, treat the call as empty. if (env.CodeInfo.MachineCode.Length == 0) { + // Increment a metric for empty calls if this is a nested call. if (!vmState.IsTopLevel) { Metrics.IncrementEmptyCalls(); @@ -710,41 +1062,63 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM goto Empty; } + // Initialize the internal stacks for the current call frame. vmState.InitializeStacks(); + + // Create an EVM stack using the current stack head, tracer, and data stack slice. EvmStack stack = new(vmState.DataStackHead, _txTracer, vmState.DataStack.AsSpan()); + + // Cache the available gas from the state for local use. long gasAvailable = vmState.GasAvailable; + // If a previous call result exists, push its bytes onto the stack. if (previousCallResult is not null) { stack.PushBytes(previousCallResult.Value.Span); - if (TTracingInstructions.IsActive) _txTracer.ReportOperationRemainingGas(vmState.GasAvailable); + + // Report the remaining gas if tracing instructions are enabled. + if (TTracingInstructions.IsActive) + { + _txTracer.ReportOperationRemainingGas(vmState.GasAvailable); + } } + // If there is previous call output, update the memory cost and save the output. if (previousCallOutput.Length > 0) { + // Use a local variable for the destination to simplify passing it by reference. UInt256 localPreviousDest = previousCallOutputDestination; + + // Attempt to update the memory cost; if insufficient gas is available, jump to the out-of-gas handler. if (!UpdateMemoryCost(vmState, ref gasAvailable, in localPreviousDest, (ulong)previousCallOutput.Length)) { goto OutOfGas; } + // Save the previous call's output into the VM state's memory. vmState.Memory.Save(in localPreviousDest, previousCallOutput); } + // Update the global EVM state with the current state. EvmState = vmState; - // Struct generic parameter is used to burn out all the if statements - // and inner code by using static property on generic IFlag using - // OnFlag or OffFlag. These checks that are evaluated to constant values at compile time. - // This only works for structs, not for classes or interface types - // which use shared generics. + + // Dispatch the bytecode interpreter. + // The second generic parameter is selected based on whether the transaction tracer is cancelable: + // - OffFlag is used when cancelation is not needed. + // - OnFlag is used when cancelation is enabled. + // This leverages the compile-time evaluation of TTracingInstructions to optimize away runtime checks. return _txTracer.IsCancelable switch { false => RunByteCode(ref stack, gasAvailable), true => RunByteCode(ref stack, gasAvailable), }; + Empty: + // Return an empty CallResult if there is no machine code to execute. return CallResult.Empty(vmState.Env.CodeInfo.Version); + OutOfGas: + // Return an out-of-gas CallResult if updating the memory cost fails. return CallResult.OutOfGasException; } From e9a5b4e31aa4ebb9a211e0c8a1a71d0037bcfbf4 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 17:47:37 +0000 Subject: [PATCH 240/255] Comments --- .../Nethermind.Evm/VirtualMachine.cs | 112 +++++++++++++----- 1 file changed, 85 insertions(+), 27 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 63aacedb36e..513d079e7c5 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1122,90 +1122,140 @@ private CallResult ExecuteCall( return CallResult.OutOfGasException; } + /// + /// Executes the EVM bytecode by iterating over the instruction set and invoking corresponding opcode methods + /// via function pointers. The method leverages compile-time evaluation of tracing and cancellation flags to optimize + /// conditional branches. It also updates the VM state as instructions are executed, handles exceptions, + /// and returns an appropriate . + /// + /// + /// A struct implementing that indicates at compile time whether tracing-specific logic should be enabled. + /// + /// + /// A struct implementing that indicates at compile time whether cancellation support is enabled. + /// + /// + /// A reference to the current EVM stack used for execution. + /// + /// + /// The amount of gas available for executing the bytecode. + /// + /// + /// A that encapsulates the outcome of the execution, which can be a successful result, + /// an empty result, a revert, or a failure due to an exception (such as out-of-gas). + /// + /// + /// The method uses an unsafe context and function pointers to invoke opcode implementations directly, + /// which minimizes overhead and allows aggressive inlining and compile-time optimizations. + /// [SkipLocalsInit] - private unsafe CallResult RunByteCode(scoped ref EvmStack stack, long gasAvailable) + private unsafe CallResult RunByteCode( + scoped ref EvmStack stack, + long gasAvailable) where TTracingInstructions : struct, IFlag where TCancelable : struct, IFlag { + // Reset return data and set the current section index from the VM state. ReturnData = null; SectionIndex = EvmState.FunctionIndex; + // Retrieve the code information and create a read-only span of instructions. ICodeInfo codeInfo = EvmState.Env.CodeInfo; ReadOnlySpan codeSection = GetInstructions(codeInfo); + // Initialize the exception type to "None". EvmExceptionType exceptionType = EvmExceptionType.None; #if DEBUG + // In debug mode, retrieve a tracer for interactive debugging. DebugTracer? debugger = _txTracer.GetTracer(); #endif - // Initialize program counter to the current state's value. - // Entry point is not always 0 as we may be returning to code after a call. + // Set the program counter from the current VM state; it may not be zero if resuming after a call. int programCounter = EvmState.ProgramCounter; - // Use fixed pointer or we loose the type when trying skip bounds check, - // and have to cast for each call (delegate*<...> can't be used as a generic arg) + + // Pin the opcode methods array to obtain a fixed pointer, avoiding repeated bounds checks and casts. + // If we don't use a pointer we have to cast for each call (delegate*<...> can't be used as a generic arg) + // Or have bounds checks (however only 256 opcodes and opcode is a byte so know always in bounds). fixed (OpCode* opcodeMethods = &_opcodeMethods[0]) { - // We use a while loop rather than a for loop as some - // opcodes can change the program counter (e.g. Push, Jump, etc) + // Iterate over the instructions using a while loop because opcodes may modify the program counter. while ((uint)programCounter < (uint)codeSection.Length) { #if DEBUG + // Allow the debugger to inspect and possibly pause execution for debugging purposes. debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); #endif - // Get the opcode at the current program counter + // Fetch the current instruction from the code section. Instruction instruction = codeSection[programCounter]; - if (TCancelable.IsActive && _txTracer.IsCancelled) ThrowOperationCanceledException(); + // If cancellation is enabled and cancellation has been requested, throw an exception. + if (TCancelable.IsActive && _txTracer.IsCancelled) + ThrowOperationCanceledException(); + + // If tracing is enabled, start an instruction trace. if (TTracingInstructions.IsActive) StartInstructionTrace(instruction, gasAvailable, programCounter, in stack); - // Advance the program counter one instruction + // Advance the program counter to point to the next instruction. programCounter++; + // For the very common POP opcode, use an inlined implementation to reduce overhead. if (Instruction.POP == instruction) { - // Very commonly called opcode and minimal implementation, so we inline here exceptionType = EvmInstructions.InstructionPop(this, ref stack, ref gasAvailable, ref programCounter); } else { - // Get the opcode delegate* from the opcode array + // Retrieve the opcode function pointer corresponding to the current instruction. OpCode opcodeMethod = opcodeMethods[(int)instruction]; - // Execute opcode delegate* via calli (see: C# function pointers https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers) - // Stack, gas, and program counter may be modified by call (also instance variables on the vm) + // Invoke the opcode method, which may modify the stack, gas, and program counter. + // Is executed using fast delegate* via calli (see: C# function pointers https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers) exceptionType = opcodeMethod(this, ref stack, ref gasAvailable, ref programCounter); } - // Exit loop if run out of gas - if (gasAvailable < 0) goto OutOfGas; - // Exit loop if exception occurred - if (exceptionType != EvmExceptionType.None) break; - // Exit loop if returning data - if (ReturnData is not null) break; - + // If gas is exhausted, jump to the out-of-gas handler. + if (gasAvailable < 0) + goto OutOfGas; + // If an exception occurred, exit the loop. + if (exceptionType != EvmExceptionType.None) + break; + // If return data has been set, exit the loop to process the returned value. + if (ReturnData is not null) + break; + + // If tracing is enabled, complete the trace for the current instruction. if (TTracingInstructions.IsActive) EndInstructionTrace(gasAvailable); } } + // Update the current VM state if no fatal exception occurred, or if the exception is of type Stop or Revert. if (exceptionType is EvmExceptionType.None or EvmExceptionType.Stop or EvmExceptionType.Revert) { UpdateCurrentState(programCounter, gasAvailable, stack.Head); } else { + // For any other exception, jump to the failure handling routine. goto ReturnFailure; } - if (exceptionType == EvmExceptionType.Revert) goto Revert; - if (ReturnData is not null) goto DataReturn; + // If the exception indicates a revert, handle it specifically. + if (exceptionType == EvmExceptionType.Revert) + goto Revert; + // If return data was produced, jump to the return data processing block. + if (ReturnData is not null) + goto DataReturn; + // If no return data is produced, return an empty call result. return CallResult.Empty(codeInfo.Version); DataReturn: #if DEBUG + // Allow debugging before processing the return data. debugger?.TryWait(ref _vmState, ref programCounter, ref gasAvailable, ref stack.Head); #endif + // Process the return data based on its runtime type. if (ReturnData is EvmState state) { return new CallResult(state); @@ -1214,14 +1264,22 @@ private unsafe CallResult RunByteCode(scoped { return new CallResult(eofCodeInfo, ReturnDataBuffer, null, codeInfo.Version); } + // Fall back to returning a CallResult with a byte array as the return data. return new CallResult(null, (byte[])ReturnData, null, codeInfo.Version); + Revert: + // Return a CallResult indicating a revert. return new CallResult(null, (byte[])ReturnData, null, codeInfo.Version, shouldRevert: true); + OutOfGas: + // Set the exception type to OutOfGas if gas has been exhausted. exceptionType = EvmExceptionType.OutOfGas; ReturnFailure: + // Return a failure CallResult based on the remaining gas and the exception type. return GetFailureReturn(gasAvailable, exceptionType); + // Converts the code section bytes into a read-only span of instructions. + // Lightest weight conversion as mostly just helpful when debugging to see what the opcodes are. static ReadOnlySpan GetInstructions(ICodeInfo codeInfo) { ReadOnlySpan codeBytes = codeInfo.CodeSection.Span; @@ -1315,11 +1373,11 @@ internal readonly ref struct CallResult 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); // TODO: use these to avoid CALL POP attacks - public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks + 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(null, default, null, fromVersion); + public static CallResult Empty(int fromVersion) => new(container: null, output: default, precompileSuccess: null, fromVersion); public CallResult(EvmState stateToExecute) { @@ -1363,7 +1421,7 @@ private CallResult(EvmExceptionType exceptionType) public (ICodeInfo Container, ReadOnlyMemory Bytes) Output { get; } public EvmExceptionType ExceptionType { get; } public bool ShouldRevert { get; } - public bool? PrecompileSuccess { get; } // TODO: check this behaviour as it seems it is required and previously that was not the case + public bool? PrecompileSuccess { get; } public bool IsReturn => StateToExecute is null; public bool IsException => ExceptionType != EvmExceptionType.None; public int FromVersion { get; } From 9c277fb7a66b981ccfff64f7c3364714c3e15a27 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 17:54:43 +0000 Subject: [PATCH 241/255] Dial back test logging --- src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 829779bfc19..611ad7d3187 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -38,8 +38,7 @@ namespace Ethereum.Test.Base; public abstract class BlockchainTestBase { - private static InterfaceLogger _logger = new NUnitLogger(LogLevel.Trace); - // private static ILogManager _logManager = new OneLoggerLogManager(_logger); + private static InterfaceLogger _logger = new NUnitLogger(LogLevel.Info); private static ILogManager _logManager = LimboLogs.Instance; private static ISealValidator Sealer { get; } private static DifficultyCalculatorWrapper DifficultyCalculator { get; } From d693d905bc8eb4a90f2aeb3c5869eace2544d987 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 18:00:25 +0000 Subject: [PATCH 242/255] Tidy up --- src/Nethermind/Nethermind.Evm/CallResult.cs | 73 +++++++++++++++++ src/Nethermind/Nethermind.Evm/TypeFlags.cs | 18 +++++ .../Nethermind.Evm/VirtualMachine.cs | 81 +------------------ 3 files changed, 92 insertions(+), 80 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/CallResult.cs create mode 100644 src/Nethermind/Nethermind.Evm/TypeFlags.cs diff --git a/src/Nethermind/Nethermind.Evm/CallResult.cs b/src/Nethermind/Nethermind.Evm/CallResult.cs new file mode 100644 index 00000000000..956f25d4b5c --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CallResult.cs @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Evm.CodeAnalysis; + +namespace Nethermind.Evm; + +public sealed unsafe partial class VirtualMachine +{ + internal 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 CallResult(EvmState stateToExecute) + { + StateToExecute = stateToExecute; + Output = (null, Array.Empty()); + PrecompileSuccess = null; + ShouldRevert = false; + ExceptionType = EvmExceptionType.None; + } + + public CallResult(ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + { + StateToExecute = null; + Output = (null, output); + PrecompileSuccess = precompileSuccess; + ShouldRevert = shouldRevert; + ExceptionType = exceptionType; + FromVersion = fromVersion; + } + + public CallResult(ICodeInfo? container, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) + { + StateToExecute = null; + Output = (container, output); + PrecompileSuccess = precompileSuccess; + ShouldRevert = shouldRevert; + ExceptionType = exceptionType; + FromVersion = fromVersion; + } + + private CallResult(EvmExceptionType exceptionType) + { + StateToExecute = null; + Output = (null, StatusCode.FailureBytes); + PrecompileSuccess = null; + ShouldRevert = false; + ExceptionType = exceptionType; + } + + public EvmState? StateToExecute { get; } + public (ICodeInfo Container, ReadOnlyMemory Bytes) Output { get; } + public EvmExceptionType ExceptionType { get; } + public bool ShouldRevert { get; } + public bool? PrecompileSuccess { get; } + public bool IsReturn => StateToExecute is null; + public bool IsException => ExceptionType != EvmExceptionType.None; + public int FromVersion { get; } + } +} diff --git a/src/Nethermind/Nethermind.Evm/TypeFlags.cs b/src/Nethermind/Nethermind.Evm/TypeFlags.cs new file mode 100644 index 00000000000..a3e1b57cc1c --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/TypeFlags.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Evm; + +public interface IFlag +{ + virtual static bool IsActive { get; } +} + +public struct OffFlag : IFlag +{ + public static bool IsActive => false; +} +public struct OnFlag : IFlag +{ + public static bool IsActive => true; +} diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 513d079e7c5..1f6f39bbbde 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1,4 +1,3 @@ - // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only @@ -20,7 +19,6 @@ using Nethermind.State; using static Nethermind.Evm.EvmObjectFormat.EofValidator; -using static Nethermind.Evm.VirtualMachine; #if DEBUG using Nethermind.Evm.Tracing.Debugger; @@ -32,7 +30,7 @@ namespace Nethermind.Evm; using unsafe OpCode = delegate*; using Int256; -public sealed unsafe class VirtualMachine( +public sealed unsafe partial class VirtualMachine( IBlockhashProvider? blockHashProvider, ISpecProvider? specProvider, ILogManager? logManager) : IVirtualMachine @@ -1363,81 +1361,4 @@ private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExc _txTracer.ReportOperationRemainingGas(gasAvailable); _txTracer.ReportOperationError(evmExceptionType); } - - internal 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 CallResult(EvmState stateToExecute) - { - StateToExecute = stateToExecute; - Output = (null, Array.Empty()); - PrecompileSuccess = null; - ShouldRevert = false; - ExceptionType = EvmExceptionType.None; - } - - public CallResult(ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) - { - StateToExecute = null; - Output = (null, output); - PrecompileSuccess = precompileSuccess; - ShouldRevert = shouldRevert; - ExceptionType = exceptionType; - FromVersion = fromVersion; - } - - public CallResult(ICodeInfo? container, ReadOnlyMemory output, bool? precompileSuccess, int fromVersion, bool shouldRevert = false, EvmExceptionType exceptionType = EvmExceptionType.None) - { - StateToExecute = null; - Output = (container, output); - PrecompileSuccess = precompileSuccess; - ShouldRevert = shouldRevert; - ExceptionType = exceptionType; - FromVersion = fromVersion; - } - - private CallResult(EvmExceptionType exceptionType) - { - StateToExecute = null; - Output = (null, StatusCode.FailureBytes); - PrecompileSuccess = null; - ShouldRevert = false; - ExceptionType = exceptionType; - } - - public EvmState? StateToExecute { get; } - public (ICodeInfo Container, ReadOnlyMemory Bytes) Output { get; } - public EvmExceptionType ExceptionType { get; } - public bool ShouldRevert { get; } - public bool? PrecompileSuccess { get; } - public bool IsReturn => StateToExecute is null; - public bool IsException => ExceptionType != EvmExceptionType.None; - public int FromVersion { get; } - } -} - -public interface IFlag -{ - virtual static bool IsActive { get; } -} - -public struct OffFlag : IFlag -{ - public static bool IsActive => false; -} -public struct OnFlag : IFlag -{ - public static bool IsActive => true; } From b24e37cb4c9169517f3191ab4062a345ab4e5b42 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 18:13:29 +0000 Subject: [PATCH 243/255] Tidy up --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 +- .../CodeAnalysis/JumpDestinationAnalyzer.cs | 404 +++++++++--------- .../Nethermind.Evm/CodeInfoFactory.cs | 4 +- .../Nethermind.Evm/CodeInfoRepository.cs | 4 +- src/Nethermind/Nethermind.Evm/ICodeInfo.cs | 2 +- .../Instructions/EvmInstructions.Call.cs | 2 +- .../Instructions/EvmInstructions.Create.cs | 2 +- .../Instructions/EvmInstructions.Eof.cs | 2 +- .../TransactionProcessor.cs | 2 +- 9 files changed, 217 insertions(+), 207 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 7f748efe405..cbe66229d79 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -49,7 +49,7 @@ void IThreadPoolWorkItem.Execute() _analyzer.Execute(); } - public void AnalyseInBackgroundIfRequired() + public void AnalyzeInBackgroundIfRequired() { if (!ReferenceEquals(_analyzer, _emptyAnalyzer) && _analyzer.RequiresAnalysis) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs index 8ed08d2a21a..26e207d894f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/JumpDestinationAnalyzer.cs @@ -8,239 +8,249 @@ using System.Runtime.Intrinsics.X86; using System.Threading; -namespace Nethermind.Evm.CodeAnalysis +namespace Nethermind.Evm.CodeAnalysis; + +public sealed class JumpDestinationAnalyzer(ReadOnlyMemory code) { - public sealed class JumpDestinationAnalyzer(ReadOnlyMemory code) - { - private const int PUSH1 = (int)Instruction.PUSH1; - private const int PUSHx = PUSH1 - 1; - private const int JUMPDEST = (int)Instruction.JUMPDEST; - private const int BitShiftPerInt64 = 6; + private const int PUSH1 = (int)Instruction.PUSH1; + private const int PUSHx = PUSH1 - 1; + private const int JUMPDEST = (int)Instruction.JUMPDEST; + private const int BitShiftPerInt64 = 6; - private static readonly long[]? _emptyJumpDestinationBitmap = new long[1]; - private long[]? _jumpDestinationBitmap = code.Length == 0 ? _emptyJumpDestinationBitmap : null; + private static readonly long[]? _emptyJumpDestinationBitmap = new long[1]; + private long[]? _jumpDestinationBitmap = code.Length == 0 ? _emptyJumpDestinationBitmap : null; - private object? _analysisComplete; - private ReadOnlyMemory MachineCode { get; } = code; + private object? _analysisComplete; + private ReadOnlyMemory MachineCode { get; } = code; - public bool ValidateJump(int destination) - { - _jumpDestinationBitmap ??= CreateOrWaitForJumpDestinationBitmap(); + public bool ValidateJump(int destination) + { + _jumpDestinationBitmap ??= CreateOrWaitForJumpDestinationBitmap(); + + // Cast to uint to change negative numbers to very int high numbers + // Then do length check, this both reduces check by 1 and eliminates the bounds + // check from accessing the span. + return (uint)destination < (uint)MachineCode.Length && IsJumpDestination(_jumpDestinationBitmap, destination); + } - // Cast to uint to change negative numbers to very int high numbers - // Then do length check, this both reduces check by 1 and eliminates the bounds - // check from accessing the span. - return (uint)destination < (uint)MachineCode.Length && IsJumpDestination(_jumpDestinationBitmap, destination); + [MethodImpl(MethodImplOptions.NoInlining)] + private long[] CreateOrWaitForJumpDestinationBitmap() + { + object? previous = Volatile.Read(ref _analysisComplete); + if (previous is null) + { + AnalyzeJumpDestinations(out previous); } - private long[] CreateOrWaitForJumpDestinationBitmap() + if (previous is ManualResetEventSlim resetEvent) { - object? previous = Volatile.Read(ref _analysisComplete); - if (previous is null) - { - ManualResetEventSlim analysisComplete = new(initialState: false); - previous = Interlocked.CompareExchange(ref _analysisComplete, analysisComplete, null); - if (previous is null) - { - // Not already in progress, so start it. - var bitmap = CreateJumpDestinationBitmap(); - _jumpDestinationBitmap = bitmap; - // Release the MRES to be GC'd - _analysisComplete = bitmap; - // Signal complete. - analysisComplete.Set(); - return bitmap; - } - } + WaitForAnalysisToComplete(resetEvent); - if (previous is ManualResetEventSlim resetEvent) - { - Thread thread = Thread.CurrentThread; - ThreadPriority priority = thread.Priority; - try - { - // We are waiting, so drop priority to normal (BlockProcessing runs at higher priority). - thread.Priority = ThreadPriority.Normal; + return _jumpDestinationBitmap; + } - // Already in progress, wait for completion. - resetEvent.Wait(); - } - finally - { - // Restore the priority of the thread. - thread.Priority = priority; - } + // Must be the bitmap, and lost check->create benign data race + return (long[])previous; + } - return _jumpDestinationBitmap; - } + private static void WaitForAnalysisToComplete(ManualResetEventSlim resetEvent) + { + Thread thread = Thread.CurrentThread; + ThreadPriority priority = thread.Priority; + try + { + // We are waiting, so drop priority to normal (BlockProcessing runs at higher priority). + thread.Priority = ThreadPriority.Normal; - // Must be the bitmap, and lost check->create benign data race - return (long[])previous; + // Already in progress, wait for completion. + resetEvent.Wait(); } + finally + { + // Restore the priority of the thread. + thread.Priority = priority; + } + } - /// - /// Used for conversion between different representations of bit array. - /// Returns (n + (64 - 1)) / 64, rearranged to avoid arithmetic overflow. - /// For example, in the bit to int case, the straightforward calc would - /// be (n + 63) / 64, but that would cause overflow. So instead it's - /// rearranged to ((n - 1) / 64) + 1. - /// Due to sign extension, we don't need to special case for n == 0, if we use - /// bitwise operations (since ((n - 1) >> 6) + 1 = 0). - /// This doesn't hold true for ((n - 1) / 64) + 1, which equals 1. - /// - /// Usage: - /// GetInt32ArrayLengthFromBitLength(77): returns how many ints must be - /// allocated to store 77 bits. - /// - /// - /// how many ints are required to store n bytes - private static int GetInt64ArrayLengthFromBitLength(int n) => - (n - 1 + (1 << BitShiftPerInt64)) >>> BitShiftPerInt64; - - /// - /// Collects data locations in code. - /// An unset bit means the byte is an opcode, a set bit means it's data. - /// - private long[] CreateJumpDestinationBitmap() + private void AnalyzeJumpDestinations(out object previous) + { + ManualResetEventSlim analysisComplete = new(initialState: false); + previous = Interlocked.CompareExchange(ref _analysisComplete, analysisComplete, null); + if (previous is null) { - Metrics.IncrementContractsAnalysed(); - ReadOnlySpan code = MachineCode.Span; - long[] jumpDestinationBitmap = new long[GetInt64ArrayLengthFromBitLength(code.Length)]; - int programCounter = 0; - // We accumulate each array segment to a register and then flush to memory when we move to next. - long currentFlags = 0; - while (true) - { - // Set default programCounter increment to 1 for default case when don't vectorize or read a PUSH. - int move = 1; - // We use Sse rather than Avx or Avx-512 as is optimization for stretch of code without PUSHes. - // As the vector size increases the chance of there being a PUSH increases which will disable this optimization. - if (Sse2.IsSupported && - // Check not going to read passed end of code. - programCounter <= code.Length - Vector128.Count && - // Are we on an short stride, one quarter of the long flags? - (programCounter & 15) == 0) - { - Vector128 data = Unsafe.As>(ref Unsafe.AddByteOffset(ref MemoryMarshal.GetReference(code), programCounter)); - // Pushes are 0x60 to 0x7f; converting to signed bytes any instruction higher than PUSH32 - // becomes negative so we can just do a single greater than test to see if any present. - Vector128 compare = Sse2.CompareGreaterThan(data, Vector128.Create((sbyte)PUSHx)); - if (compare == default) - { - // Check the bytes for any JUMPDESTs. - Vector128 dest = Sse2.CompareEqual(data, Vector128.Create((sbyte)JUMPDEST)); - // Extract the checks as a set of int flags. - int flags = Sse2.MoveMask(dest); - // Shift up flags by depending which side of long we are on, and merge to current set. - currentFlags |= (long)flags << (programCounter & (32 + 16)); - // Forward programCounter by Vector128 stride. - move = Vector128.Count; - goto Next; - } - } + // Not already in progress, so start it. + var bitmap = CreateJumpDestinationBitmap(); + _jumpDestinationBitmap = bitmap; + // Release the MRES to be GC'd + _analysisComplete = bitmap; + // Signal complete. + analysisComplete.Set(); + previous = bitmap; + } + } - // Grab the instruction from the code; zero length code - // doesn't enter this method and we check at end of loop if - // hit the last element and should exit, so skip bounds check - // access here. - int op = Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter); + /// + /// Used for conversion between different representations of bit array. + /// Returns (n + (64 - 1)) / 64, rearranged to avoid arithmetic overflow. + /// For example, in the bit to int case, the straightforward calc would + /// be (n + 63) / 64, but that would cause overflow. So instead it's + /// rearranged to ((n - 1) / 64) + 1. + /// Due to sign extension, we don't need to special case for n == 0, if we use + /// bitwise operations (since ((n - 1) >> 6) + 1 = 0). + /// This doesn't hold true for ((n - 1) / 64) + 1, which equals 1. + /// + /// Usage: + /// GetInt32ArrayLengthFromBitLength(77): returns how many ints must be + /// allocated to store 77 bits. + /// + /// + /// how many ints are required to store n bytes + private static int GetInt64ArrayLengthFromBitLength(int n) => + (n - 1 + (1 << BitShiftPerInt64)) >>> BitShiftPerInt64; - if (op == JUMPDEST) - { - // Accumulate Jump Destinations to register, shift will wrap and single bit - // so can shift by the whole programCounter. - currentFlags |= 1L << programCounter; - } - else if ((sbyte)op > PUSHx) + /// + /// Collects data locations in code. + /// An unset bit means the byte is an opcode, a set bit means it's data. + /// + private long[] CreateJumpDestinationBitmap() + { + Metrics.IncrementContractsAnalysed(); + ReadOnlySpan code = MachineCode.Span; + long[] jumpDestinationBitmap = new long[GetInt64ArrayLengthFromBitLength(code.Length)]; + int programCounter = 0; + // We accumulate each array segment to a register and then flush to memory when we move to next. + long currentFlags = 0; + while (true) + { + // Set default programCounter increment to 1 for default case when don't vectorize or read a PUSH. + int move = 1; + // We use Sse rather than Avx or Avx-512 as is optimization for stretch of code without PUSHes. + // As the vector size increases the chance of there being a PUSH increases which will disable this optimization. + if (Sse2.IsSupported && + // Check not going to read passed end of code. + programCounter <= code.Length - Vector128.Count && + // Are we on an short stride, one quarter of the long flags? + (programCounter & 15) == 0) + { + Vector128 data = Unsafe.As>(ref Unsafe.AddByteOffset(ref MemoryMarshal.GetReference(code), programCounter)); + // Pushes are 0x60 to 0x7f; converting to signed bytes any instruction higher than PUSH32 + // becomes negative so we can just do a single greater than test to see if any present. + Vector128 compare = Sse2.CompareGreaterThan(data, Vector128.Create((sbyte)PUSHx)); + if (compare == default) { - // Fast forward programCounter by the amount of data the push - // represents as don't need to analyse data for Jump Destinations. - move = op - PUSH1 + 2; + // Check the bytes for any JUMPDESTs. + Vector128 dest = Sse2.CompareEqual(data, Vector128.Create((sbyte)JUMPDEST)); + // Extract the checks as a set of int flags. + int flags = Sse2.MoveMask(dest); + // Shift up flags by depending which side of long we are on, and merge to current set. + currentFlags |= (long)flags << (programCounter & (32 + 16)); + // Forward programCounter by Vector128 stride. + move = Vector128.Count; + goto Next; } + } - Next: - int nextCounter = programCounter + move; - // Check if read last item of code; we want to write this out also even if not - // at a boundary and then we will return the results. - bool exit = nextCounter >= code.Length; - // Does the move mean we are moving to new segment of the long array? - // If we take the current index in flags, and add the move, are we at - // a new long segment, i.e. a larger than 64 position move. - if ((programCounter & 63) + move >= 64 || exit) - { - // If so write out the flags (if any are set) - if (currentFlags != 0) - { - // Moving to next array element (or finishing) assign to array. - MarkJumpDestinations(jumpDestinationBitmap, programCounter, currentFlags); - // Clear the flags in preparation for the next array segment. - currentFlags = 0; - } - } + // Grab the instruction from the code; zero length code + // doesn't enter this method and we check at end of loop if + // hit the last element and should exit, so skip bounds check + // access here. + int op = Unsafe.Add(ref MemoryMarshal.GetReference(code), programCounter); - if (exit) + if (op == JUMPDEST) + { + // Accumulate Jump Destinations to register, shift will wrap and single bit + // so can shift by the whole programCounter. + currentFlags |= 1L << programCounter; + } + else if ((sbyte)op > PUSHx) + { + // Fast forward programCounter by the amount of data the push + // represents as don't need to analyse data for Jump Destinations. + move = op - PUSH1 + 2; + } + + Next: + int nextCounter = programCounter + move; + // Check if read last item of code; we want to write this out also even if not + // at a boundary and then we will return the results. + bool exit = nextCounter >= code.Length; + // Does the move mean we are moving to new segment of the long array? + // If we take the current index in flags, and add the move, are we at + // a new long segment, i.e. a larger than 64 position move. + if ((programCounter & 63) + move >= 64 || exit) + { + // If so write out the flags (if any are set) + if (currentFlags != 0) { - // End of code. - break; + // Moving to next array element (or finishing) assign to array. + MarkJumpDestinations(jumpDestinationBitmap, programCounter, currentFlags); + // Clear the flags in preparation for the next array segment. + currentFlags = 0; } + } - // Move to next instruction. - programCounter = nextCounter; + if (exit) + { + // End of code. + break; } - return jumpDestinationBitmap; + // Move to next instruction. + programCounter = nextCounter; } - /// - /// Checks if the position is in a code segment. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsJumpDestination(long[] bitvec, int pos) - { - int vecIndex = pos >> BitShiftPerInt64; - // Check if in bounds, Jit will add slightly more expensive exception throwing check if we don't. - if ((uint)vecIndex >= (uint)bitvec.Length) return false; + return jumpDestinationBitmap; + } - return (bitvec[vecIndex] & (1L << pos)) != 0; - } + /// + /// Checks if the position is in a code segment. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsJumpDestination(long[] bitvec, int pos) + { + int vecIndex = pos >> BitShiftPerInt64; + // Check if in bounds, Jit will add slightly more expensive exception throwing check if we don't. + if ((uint)vecIndex >= (uint)bitvec.Length) return false; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void MarkJumpDestinations(long[] jumpDestinationBitmap, int pos, long flags) - { - uint offset = (uint)pos >> BitShiftPerInt64; - Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(jumpDestinationBitmap), offset) |= flags; - } + return (bitvec[vecIndex] & (1L << pos)) != 0; + } - public void Execute() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MarkJumpDestinations(long[] jumpDestinationBitmap, int pos, long flags) + { + uint offset = (uint)pos >> BitShiftPerInt64; + Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(jumpDestinationBitmap), offset) |= flags; + } + + public void Execute() + { + if (_jumpDestinationBitmap is null && Volatile.Read(ref _analysisComplete) is null) { - if (_jumpDestinationBitmap is null && Volatile.Read(ref _analysisComplete) is null) + ManualResetEventSlim analysisComplete = new(initialState: false); + if (Interlocked.CompareExchange(ref _analysisComplete, analysisComplete, null) is null) { - ManualResetEventSlim analysisComplete = new(initialState: false); - if (Interlocked.CompareExchange(ref _analysisComplete, analysisComplete, null) is null) + Thread thread = Thread.CurrentThread; + ThreadPriority priority = thread.Priority; + try + { + // Boost the priority of the thread as block processing may be waiting on this. + thread.Priority = ThreadPriority.AboveNormal; + + _jumpDestinationBitmap ??= CreateJumpDestinationBitmap(); + + // Release the MRES to be GC'd + _analysisComplete = _jumpDestinationBitmap; + // Signal complete. + analysisComplete.Set(); + } + finally { - Thread thread = Thread.CurrentThread; - ThreadPriority priority = thread.Priority; - try - { - // Boost the priority of the thread as block processing may be waiting on this. - thread.Priority = ThreadPriority.AboveNormal; - - _jumpDestinationBitmap ??= CreateJumpDestinationBitmap(); - - // Release the MRES to be GC'd - _analysisComplete = _jumpDestinationBitmap; - // Signal complete. - analysisComplete.Set(); - } - finally - { - // Restore the priority of the thread. - thread.Priority = priority; - } + // Restore the priority of the thread. + thread.Priority = priority; } } } - - public bool RequiresAnalysis => _jumpDestinationBitmap is null; } + + public bool RequiresAnalysis => _jumpDestinationBitmap is null; } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index 68c8bf511b1..2d3b48a9366 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -19,7 +19,7 @@ public static ICodeInfo CreateCodeInfo(ReadOnlyMemory code, IReleaseSpec s return new EofCodeInfo(container.Value); } } - codeInfo.AnalyseInBackgroundIfRequired(); + codeInfo.AnalyzeInBackgroundIfRequired(); return codeInfo; } @@ -39,7 +39,7 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out return false; } codeInfo = new CodeInfo(data); - codeInfo.AnalyseInBackgroundIfRequired(); + codeInfo.AnalyzeInBackgroundIfRequired(); return true; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index fe05954fd1c..8a7d24bc61e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -106,7 +106,7 @@ private static ICodeInfo InternalGetCachedCode(IReadOnlyStateProvider worldState MissingCode(codeSource, codeHash); } - cachedCodeInfo = CodeInfoFactory.CreateCodeInfo(code, vmSpec, Nethermind.Evm.EvmObjectFormat.ValidationStrategy.ExractHeader); + cachedCodeInfo = CodeInfoFactory.CreateCodeInfo(code, vmSpec, ValidationStrategy.ExractHeader); _codeCache.Set(codeHash, cachedCodeInfo); } else @@ -127,7 +127,7 @@ static void MissingCode(Address codeSource, in ValueHash256 codeHash) public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec, ValidationStrategy.ExractHeader); - codeInfo.AnalyseInBackgroundIfRequired(); + codeInfo.AnalyzeInBackgroundIfRequired(); ValueHash256 codeHash = code.Length == 0 ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(code.Span); state.InsertCode(codeOwner, codeHash, code, spec); diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs index eba187af25e..09ff6c97a97 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfo.cs @@ -22,6 +22,6 @@ public interface ICodeInfo SectionHeader? ContainerSectionOffset(int idx); int PcOffset(); (byte inputCount, byte outputCount, ushort maxStackHeight) GetSectionMetadata(int index) => (0, 0, 1024); - void AnalyseInBackgroundIfRequired() { } + void AnalyzeInBackgroundIfRequired() { } bool ValidateJump(int destination); } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 8a503813c3c..455970fa59a 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -178,7 +178,7 @@ public static EvmExceptionType InstructionCall( // Retrieve code information for the call and schedule background analysis if needed. ICodeInfo codeInfo = vm.CodeInfoRepository.GetCachedCodeInfo(state, codeSource, spec); - codeInfo.AnalyseInBackgroundIfRequired(); + codeInfo.AnalyzeInBackgroundIfRequired(); // Apply the 63/64 gas rule if enabled. if (spec.Use63Over64Rule) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 83e04009f02..682a7f63aec 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -200,7 +200,7 @@ public static EvmExceptionType InstructionCreate Date: Wed, 5 Feb 2025 18:15:47 +0000 Subject: [PATCH 244/255] Dial back state test loggin --- src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs index 390afb5b46b..cc07c797b12 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs @@ -31,7 +31,7 @@ namespace Ethereum.Test.Base { public abstract class GeneralStateTestBase { - private static ILogger _logger = new(new NUnitLogger(LogLevel.Trace)); + private static ILogger _logger = new(new NUnitLogger(LogLevel.Info)); private static ILogManager _logManager = new TestLogManager(LogLevel.Warn); private static readonly UInt256 _defaultBaseFeeForStateTest = 0xA; private readonly TxValidator _txValidator = new(MainnetSpecProvider.Instance.ChainId); From 0910445bb733656cf7238d3d1ae1327bca5aa28c Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 19:37:58 +0000 Subject: [PATCH 245/255] Improve code creation --- src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs | 12 +++++++----- .../Instructions/EvmInstructions.Create.cs | 1 - .../TransactionProcessing/TransactionProcessor.cs | 2 +- src/Nethermind/Nethermind.State/IWorldState.cs | 2 +- src/Nethermind/Nethermind.State/StateProvider.cs | 9 ++++++++- src/Nethermind/Nethermind.State/WorldState.cs | 4 ++-- .../Nethermind.State/WorldStateMetricsDecorator.cs | 2 +- 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 8a7d24bc61e..450f7eb7f4c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -126,12 +126,14 @@ static void MissingCode(Address codeSource, in ValueHash256 codeHash) public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec, ValidationStrategy.ExractHeader); - codeInfo.AnalyzeInBackgroundIfRequired(); - ValueHash256 codeHash = code.Length == 0 ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(code.Span); - state.InsertCode(codeOwner, codeHash, code, spec); - _codeCache.Set(codeHash, codeInfo); + // If the code is already in the cache, we don't need to create and add it again + if (state.InsertCode(codeOwner, in codeHash, code, spec) || + _codeCache.Get(in codeHash) is null) + { + ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec, ValidationStrategy.ExractHeader); + _codeCache.Set(in codeHash, codeInfo); + } } public void SetDelegation(IWorldState state, Address codeSource, Address authority, IReleaseSpec spec) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 682a7f63aec..c944c96d1c1 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -200,7 +200,6 @@ public static EvmExceptionType InstructionCreate, IReadOnlyStateProvider void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default); void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default); - void InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false); + bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false); void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec); diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index bc4ab7ae3c9..5e3ee104e1f 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -144,9 +144,10 @@ public UInt256 GetBalance(Address address) return account?.Balance ?? UInt256.Zero; } - public void InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) + public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) { _needsStateRootUpdate = true; + bool inserted = false; // Don't reinsert if already inserted. This can be the case when the same // code is used by multiple deployments. Either from factory contracts (e.g. LPs) @@ -170,6 +171,10 @@ public void InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory _codeInsertFilter.Set(codeHash); } + else + { + inserted = true; + } Account? account = GetThroughCache(address) ?? throw new InvalidOperationException($"Account {address} is null when updating code hash"); if (account.CodeHash.ValueHash256 != codeHash) @@ -186,6 +191,8 @@ public void InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory PushTouch(address, account, spec, account.Balance.IsZero); } } + + return inserted; } private void SetNewBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, bool isSubtracting) diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index c95ad044771..112fadfe42f 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -143,9 +143,9 @@ public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce { _stateProvider.CreateAccount(address, balance, nonce); } - public void InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) + public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) { - _stateProvider.InsertCode(address, codeHash, code, spec, isGenesis); + return _stateProvider.InsertCode(address, codeHash, code, spec, isGenesis); } public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) { diff --git a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs index ebb46cf7fc4..aa0fe0bb4b8 100644 --- a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs +++ b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs @@ -66,7 +66,7 @@ public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) => innerState.CreateAccountIfNotExists(address, in balance, in nonce); - public void InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => + public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => innerState.InsertCode(address, in codeHash, code, spec, isGenesis); public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => From b14808e37fac7eeed04857358009fb7c148d20c5 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 19:46:00 +0000 Subject: [PATCH 246/255] Improve --- src/Nethermind/Nethermind.State/StateProvider.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 5e3ee104e1f..ea50844b1b4 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -170,9 +170,6 @@ public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory } _codeInsertFilter.Set(codeHash); - } - else - { inserted = true; } From 9cd3b37001b63096049aace415725c34519de219 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 19:50:18 +0000 Subject: [PATCH 247/255] More lazy --- src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 450f7eb7f4c..081f10ab116 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -127,8 +127,8 @@ static void MissingCode(Address codeSource, in ValueHash256 codeHash) public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { ValueHash256 codeHash = code.Length == 0 ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(code.Span); - // If the code is already in the cache, we don't need to create and add it again - if (state.InsertCode(codeOwner, in codeHash, code, spec) || + // If the code is already in the cache, we don't need to create and add it again (and reanalyze it) + if (state.InsertCode(codeOwner, in codeHash, code, spec) && _codeCache.Get(in codeHash) is null) { ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(code, spec, ValidationStrategy.ExractHeader); From 758d101e025ff7a6aa0b2cc74088f988a69fc725 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 19:56:06 +0000 Subject: [PATCH 248/255] Others --- src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 081f10ab116..2910dd61596 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -147,8 +147,12 @@ public void SetDelegation(IWorldState state, Address codeSource, Address authori Eip7702Constants.DelegationHeader.CopyTo(authorizedBuffer); codeSource.Bytes.CopyTo(authorizedBuffer, Eip7702Constants.DelegationHeader.Length); ValueHash256 codeHash = ValueKeccak.Compute(authorizedBuffer); - state.InsertCode(authority, codeHash, authorizedBuffer.AsMemory(), spec); - _codeCache.Set(codeHash, new CodeInfo(authorizedBuffer)); + // If the code is already in the cache, we don't need to create and add it again (and reanalyze it) + if (state.InsertCode(authority, codeHash, authorizedBuffer.AsMemory(), spec) && + _codeCache.Get(in codeHash) is null) + { + _codeCache.Set(codeHash, new CodeInfo(authorizedBuffer)); + } } /// From e9fcdb1984ecd4f8f3e4a2a27287cb4d32201b58 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 5 Feb 2025 20:43:20 +0000 Subject: [PATCH 249/255] Refatcor --- .../Nethermind.Core/Collections/JournalCollection.cs | 2 +- .../Nethermind.Core/Collections/JournalSet.cs | 2 +- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 +- .../Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs | 2 +- .../Instructions/EvmInstructions.Bitwise.cs | 2 +- .../Instructions/EvmInstructions.Call.cs | 2 +- .../Instructions/EvmInstructions.CodeCopy.cs | 2 +- .../Instructions/EvmInstructions.ControlFlow.cs | 2 +- .../Instructions/EvmInstructions.Create.cs | 2 +- .../Instructions/EvmInstructions.Crypto.cs | 5 +---- .../Instructions/EvmInstructions.Environment.cs | 3 +-- .../Instructions/EvmInstructions.Eof.cs | 2 +- .../Instructions/EvmInstructions.Math1Param.cs | 2 +- .../Instructions/EvmInstructions.Math2Param.cs | 2 +- .../Instructions/EvmInstructions.Math3Param.cs | 2 +- .../Instructions/EvmInstructions.Shifts.cs | 2 +- .../Instructions/EvmInstructions.Stack.cs | 2 +- .../Instructions/EvmInstructions.Storage.cs | 2 +- .../Nethermind.Evm/Instructions/EvmInstructions.cs | 2 +- src/Nethermind/Nethermind.Evm/StackAccessTracker.cs | 12 ++++++------ src/Nethermind/Nethermind.Evm/StackPool.cs | 2 +- 21 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Collections/JournalCollection.cs b/src/Nethermind/Nethermind.Core/Collections/JournalCollection.cs index 389b72c1774..37f70a9afda 100644 --- a/src/Nethermind/Nethermind.Core/Collections/JournalCollection.cs +++ b/src/Nethermind/Nethermind.Core/Collections/JournalCollection.cs @@ -12,7 +12,7 @@ namespace Nethermind.Core.Collections /// /// Item type. /// Due to snapshots is not supported. - public class JournalCollection : ICollection, IReadOnlyCollection, IJournal + public sealed class JournalCollection : ICollection, IReadOnlyCollection, IJournal { private readonly List _list = new(); public int TakeSnapshot() => Count - 1; diff --git a/src/Nethermind/Nethermind.Core/Collections/JournalSet.cs b/src/Nethermind/Nethermind.Core/Collections/JournalSet.cs index b215e408639..6be497ff646 100644 --- a/src/Nethermind/Nethermind.Core/Collections/JournalSet.cs +++ b/src/Nethermind/Nethermind.Core/Collections/JournalSet.cs @@ -12,7 +12,7 @@ namespace Nethermind.Core.Collections /// /// Item type. /// Due to snapshots is not supported. - public class JournalSet : IReadOnlySet, ICollection, IJournal + public sealed class JournalSet : IReadOnlySet, ICollection, IJournal { private readonly List _items = new(); private readonly HashSet _set = new(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index cbe66229d79..59467a6f8f1 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -9,7 +9,7 @@ namespace Nethermind.Evm.CodeAnalysis; -public class CodeInfo : ICodeInfo, IThreadPoolWorkItem +public sealed class CodeInfo : ICodeInfo, IThreadPoolWorkItem { public ReadOnlyMemory MachineCode { get; } public IPrecompile? Precompile { get; set; } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs index 7adef13124e..04907bcbaa4 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeInfo.cs @@ -9,7 +9,7 @@ namespace Nethermind.Evm.CodeAnalysis; -public class EofCodeInfo(in EofContainer container) : ICodeInfo +public sealed class EofCodeInfo(in EofContainer container) : ICodeInfo { public EofContainer EofContainer { get; private set; } = container; public ReadOnlyMemory MachineCode => EofContainer.Container; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs index 7998d853cea..6853521d480 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs @@ -9,7 +9,7 @@ namespace Nethermind.Evm; using Word = Vector256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Represents a bitwise operation on 256-bit vectors. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 455970fa59a..9c37d77300b 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -13,7 +13,7 @@ namespace Nethermind.Evm; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Interface defining the properties for a call-like opcode. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 23c0e8f9ea9..95230a112f3 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -10,7 +10,7 @@ namespace Nethermind.Evm; using Int256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Provides a mechanism to retrieve a code segment for code copy operations. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs index 5d584540bc0..94dc4831ddf 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs @@ -11,7 +11,7 @@ namespace Nethermind.Evm; using Int256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Pushes the current program counter (minus one) onto the EVM stack. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index c944c96d1c1..1d7b62fe670 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -17,7 +17,7 @@ namespace Nethermind.Evm; /// /// Contains implementations for EVM instructions including contract creation (CREATE and CREATE2). /// -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Interface for CREATE opcode types. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs index c1c43667e5b..9db6ea61b7b 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs @@ -5,16 +5,13 @@ using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using Nethermind.Core.Crypto; -using Nethermind.Core.Specs; namespace Nethermind.Evm; -using static Nethermind.Evm.VirtualMachine; using Int256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { - /// /// Computes the Keccak-256 hash of a specified memory region. /// Pops a memory offset and length from the stack, charges gas based on the data size, diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index b32ddc13cc2..ac6cc043925 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -9,7 +9,6 @@ using Nethermind.Core.Specs; using Nethermind.Core.Crypto; using Nethermind.Evm.EvmObjectFormat; -using Nethermind.Evm.Precompiles; using Nethermind.State; using static Nethermind.Evm.VirtualMachine; @@ -17,7 +16,7 @@ namespace Nethermind.Evm; using Int256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Defines an environment introspection operation that returns a byte span. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 8a3f7cb3ff0..d4b0bc575e6 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -16,7 +16,7 @@ namespace Nethermind.Evm; using Int256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Interface defining properties for an EOF call instruction. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs index 3cf2f43d3c2..77b92a9290b 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs @@ -11,7 +11,7 @@ namespace Nethermind.Evm; using Word = Vector256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Interface for single-parameter mathematical operations on 256‐bit vectors. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs index 9932fc2d773..6620da08f40 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs @@ -10,7 +10,7 @@ namespace Nethermind.Evm; using Int256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Interface for two-parameter mathematical operations on 256-bit unsigned integers. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs index f70ca24ce1c..781a0162ff2 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs @@ -6,7 +6,7 @@ namespace Nethermind.Evm; using Int256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { public interface IOpMath3Param { diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs index aab9c247151..e574a8bd58d 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Shifts.cs @@ -7,7 +7,7 @@ namespace Nethermind.Evm; using Int256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Interface for shift operations. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index 813f1bc80cf..70e2918db96 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -12,7 +12,7 @@ namespace Nethermind.Evm; using Int256; using Word = Vector256; -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Pops a value from the EVM stack. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index 0df3142f733..a59e922d779 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -14,7 +14,7 @@ namespace Nethermind.Evm; /// /// Implements various EVM instruction handlers for transient storage, memory, and persistent storage operations. /// -internal sealed partial class EvmInstructions +internal static partial class EvmInstructions { /// /// Enumeration for specifying the type of storage access. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs index 5186f12cb1a..f39ac046202 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.cs @@ -10,7 +10,7 @@ namespace Nethermind.Evm; using Int256; using Nethermind.Evm.Precompiles; -internal unsafe sealed partial class EvmInstructions +internal unsafe static partial class EvmInstructions { /// /// Generates the opcode lookup table for the Ethereum Virtual Machine. diff --git a/src/Nethermind/Nethermind.Evm/StackAccessTracker.cs b/src/Nethermind/Nethermind.Evm/StackAccessTracker.cs index 92d3a4a189e..7a63c142f13 100644 --- a/src/Nethermind/Nethermind.Evm/StackAccessTracker.cs +++ b/src/Nethermind/Nethermind.Evm/StackAccessTracker.cs @@ -13,11 +13,11 @@ namespace Nethermind.Evm; public struct StackAccessTracker : IDisposable { - public readonly IReadOnlySet
AccessedAddresses => _trackingState.AccessedAddresses; - public readonly IReadOnlySet AccessedStorageCells => _trackingState.AccessedStorageCells; - public readonly ICollection Logs => _trackingState.Logs; - public readonly IReadOnlySet
DestroyList => _trackingState.DestroyList; - public readonly IReadOnlySet CreateList => _trackingState.CreateList; + public readonly JournalSet
AccessedAddresses => _trackingState.AccessedAddresses; + public readonly JournalSet AccessedStorageCells => _trackingState.AccessedStorageCells; + public readonly JournalCollection Logs => _trackingState.Logs; + public readonly JournalSet
DestroyList => _trackingState.DestroyList; + public readonly HashSet CreateList => _trackingState.CreateList; private TrackingState _trackingState; @@ -97,7 +97,7 @@ public void Dispose() TrackingState.ResetAndReturn(state); } - private class TrackingState + private sealed class TrackingState { private static readonly ConcurrentQueue _trackerPool = new(); public static TrackingState RentState() => _trackerPool.TryDequeue(out TrackingState tracker) ? tracker : new TrackingState(); diff --git a/src/Nethermind/Nethermind.Evm/StackPool.cs b/src/Nethermind/Nethermind.Evm/StackPool.cs index 1564e8c4c86..6eb9894143b 100644 --- a/src/Nethermind/Nethermind.Evm/StackPool.cs +++ b/src/Nethermind/Nethermind.Evm/StackPool.cs @@ -7,7 +7,7 @@ namespace Nethermind.Evm; -internal class StackPool +internal sealed class StackPool { // Also have parallel prewarming and Rpc calls private const int MaxStacksPooled = VirtualMachine.MaxCallDepth * 2; From aef435b6567cda64ed9606ce9e79b4e1bab9cb1d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 6 Feb 2025 00:53:09 +0000 Subject: [PATCH 250/255] Tidy up --- .../EvmObjectFormat/Handlers/EofV1.cs | 8 ++--- .../Nethermind.Evm/VirtualMachine.cs | 36 +++++++++++-------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 39d6b952775..bc4b5f4010e 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -503,7 +503,7 @@ private bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS + 1) { // move this check where `header.ExtraContainers.Count` is parsed - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, initcode Containers count must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSections?.Count}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, InitCode Containers count must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSections?.Count}"); return false; } @@ -537,10 +537,10 @@ private bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida return false; } - ReadOnlySpan typesection = container.Slice(typeSectionStart, typeSectionSize); - if (!ValidateTypeSection(typesection)) + ReadOnlySpan typeSectionBytes = container.Slice(typeSectionStart, typeSectionSize); + if (!ValidateTypeSection(typeSectionBytes)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, invalid typesection found"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, invalid TypeSection found"); return false; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 1f6f39bbbde..4953f2d2bb0 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -37,7 +37,7 @@ public sealed unsafe partial class VirtualMachine( { public const int MaxCallDepth = Eof1.RETURN_STACK_MAX_HEIGHT; private readonly static UInt256 P255Int = (UInt256)System.Numerics.BigInteger.Pow(2, 255); - internal readonly static byte[] EofHash256 = KeccakHash.ComputeHashBytes(EofValidator.MAGIC); + internal readonly static byte[] EofHash256 = KeccakHash.ComputeHashBytes(MAGIC); internal static ref readonly UInt256 P255 => ref P255Int; internal static readonly UInt256 BigInt256 = 256; internal static readonly UInt256 BigInt32 = 32; @@ -333,7 +333,6 @@ private void HandleEofCreate(in CallResult callResult, EvmState previousState, l // 1 - load deploy EOF subcontainer at deploy_container_index in the container from which RETURNCONTRACT is executed ReadOnlySpan auxExtraData = callResult.Output.Bytes.Span; EofCodeInfo deployCodeInfo = (EofCodeInfo)callResult.Output.Container; - byte[] bytecodeResultArray = null; // 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.MachineCode.Length + auxExtraData.Length]; @@ -343,20 +342,29 @@ private void HandleEofCreate(in CallResult callResult, EvmState previousState, l auxExtraData.CopyTo(bytecodeResult[deployCodeInfo.MachineCode.Length..]); // 2 - 2 - update data section size in the header u16 - int dataSubheaderSectionStart = - EofValidator.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) + 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 - ? 0 // container section : (0 bytes if no container section is available) - : ONE_BYTE_LENGTH + TWO_BYTE_LENGTH + TWO_BYTE_LENGTH * deployCodeInfo.EofContainer.Header.ContainerSections.Value.Count) // container section : (1 byte of separator + (ContainerSections count) * 2 bytes for size) - + ONE_BYTE_LENGTH; // data section seperator + // 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); + bytecodeResult[dataSubHeaderSectionStart + 1] = (byte)(dataSize >> 8); + bytecodeResult[dataSubHeaderSectionStart + 2] = (byte)(dataSize & 0xFF); - bytecodeResultArray = bytecodeResult.ToArray(); + byte[] bytecodeResultArray = bytecodeResult.ToArray(); // 3 - if updated deploy container size exceeds MAX_CODE_SIZE instruction exceptionally aborts bool invalidCode = bytecodeResultArray.Length > spec.MaxCodeSize; @@ -753,14 +761,14 @@ private CallResult ExecutePrecompile(EvmState currentState, bool isTracingAction // If the failure is due to an exception (e.g., out-of-gas), set the corresponding failure exception. if (callResult.IsException) { - failure = VirtualMachine.PrecompileOutOfGasException; + failure = PrecompileOutOfGasException; goto Failure; } // If running a precompile on a top-level call frame and it fails, assign a general execution failure. if (currentState.IsPrecompile && currentState.IsTopLevel) { - failure = VirtualMachine.PrecompileExecutionFailureException; + failure = PrecompileExecutionFailureException; goto Failure; } From 025506f08926136deda202611d95f09d0b2d112c Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 6 Feb 2025 02:46:36 +0000 Subject: [PATCH 251/255] Refactor --- .../EvmObjectFormat/EofCodeValidator.cs | 2 +- .../EvmObjectFormat/Handlers/EofV1.cs | 805 ++++++++++++------ .../EvmObjectFormat/IEofVersionHandler.cs | 2 +- 3 files changed, 527 insertions(+), 282 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs index a6e1b06874e..ff7b8adc131 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofCodeValidator.cs @@ -86,7 +86,7 @@ public static bool IsValidEof(ReadOnlyMemory code, ValidationStrategy stra if (IsEof(code, out byte version) && _eofVersionHandlers.TryGetValue(version, out IEofVersionHandler handler)) { - return handler.TryGetEofContainer(code, strategy, out eofContainer); + return handler.TryGetEofContainer(strategy, out eofContainer, code); } if (Logger.IsTrace) Logger.Trace($"EOF: Not EOF"); diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index bc4b5f4010e..97cb32599a2 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using DotNetty.Common.Utilities; using FastEnumUtility; using Nethermind.Core.Extensions; using Nethermind.Evm; @@ -19,69 +18,7 @@ namespace Nethermind.Evm.EvmObjectFormat.Handlers; internal class Eof1 : IEofVersionHandler { - private readonly struct QueueManager - { - public readonly Queue<(int index, ValidationStrategy strategy)> ContainerQueue; - public readonly ValidationStrategy[] VisitedContainers; - - public QueueManager(int containerCount) - { - ContainerQueue = new(); - 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 IsAllVisited() => VisitedContainers.All(x => x != 0); - } - - [StructLayout(LayoutKind.Sequential)] - private struct StackBounds() - { - public short Max = -1; - public short Min = 1023; - - public void Combine(StackBounds other) - { - this.Max = Math.Max(this.Max, other.Max); - this.Min = Math.Min(this.Min, other.Min); - } - - public readonly bool BoundsEqual() => Max == Min; - - public static bool operator ==(StackBounds left, StackBounds right) => left.Max == right.Max && right.Min == left.Min; - public static bool operator !=(StackBounds left, StackBounds right) => !(left == right); - public override readonly bool Equals(object obj) => obj is StackBounds bounds && this == bounds; - public override readonly int GetHashCode() => Max ^ Min; - } - - private ref struct Sizes - { - public ushort? TypeSectionSize; - public ushort? CodeSectionSize; - public ushort? DataSectionSize; - public ushort? ContainerSectionSize; - } - public const byte VERSION = 0x01; - internal enum Separator : byte - { - KIND_TYPE = 0x01, - KIND_CODE = 0x02, - KIND_CONTAINER = 0x03, - KIND_DATA = 0x04, - TERMINATOR = 0x00 - } internal const byte MINIMUM_HEADER_SECTION_SIZE = 3; internal const byte MINIMUM_TYPESECTION_SIZE = 4; @@ -121,240 +58,178 @@ internal enum Separator : byte // 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) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static ushort GetUInt16(ReadOnlySpan container, int offset) => - container.Slice(offset, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); - - ReadOnlySpan container = containerMemory.Span; - header = null; - // we need to be able to parse header + minimum section lengths - 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; - } + ReadOnlySpan container = containerMemory.Span; - if (!container.StartsWith(EofValidator.MAGIC)) + // Validate overall container size, magic value, and version. + if (!ValidateBasicConstraints(container)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code doesn't start with magic byte sequence expected {EofValidator.MAGIC.ToHexString(true)} "); return false; } - if (container[EofValidator.VERSION_OFFSET] != VERSION) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not Eof version {VERSION}"); - return false; - } + // The current read position; after the version byte. + int pos = EofValidator.VERSION_OFFSET + 1; + // Holds header size information for each section. Sizes sectionSizes = new(); - int[] codeSections = null; - int[] containerSections = null; - int pos = EofValidator.VERSION_OFFSET + 1; - var continueParsing = true; + // These arrays hold the sizes for compound sections. + int[]? codeSections = null; + int[]? containerSections = null; + + bool continueParsing = true; while (continueParsing && pos < container.Length) { - var separator = (Separator)container[pos++]; - + // 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 (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Multiple type sections"); return false; } - - sectionSizes.TypeSectionSize = GetUInt16(container, pos); - if (sectionSizes.TypeSectionSize < MINIMUM_TYPESECTION_SIZE) + if (!TryParseTypeSection(ref pos, out ushort typeSectionSize, container)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, TypeSection Size must be at least 3, but found {sectionSizes.TypeSectionSize}"); return false; } - - pos += EofValidator.TWO_BYTE_LENGTH; + sectionSizes.TypeSectionSize = typeSectionSize; break; + case Separator.KIND_CODE: if (sectionSizes.CodeSectionSize != null) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Multiple code sections"); + 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 (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); - return false; - } - - var numberOfCodeSections = GetUInt16(container, pos); - sectionSizes.CodeSectionSize = (ushort)(numberOfCodeSections * EofValidator.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}"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); return false; } - - if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfCodeSections) + if (!TryParseCodeSection(ref pos, out codeSections, out ushort codeHeaderSize, container)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); return false; } - - codeSections = new int[numberOfCodeSections]; - int CODESECTION_HEADER_PREFIX_SIZE = pos + EofValidator.TWO_BYTE_LENGTH; - for (ushort i = 0; i < codeSections.Length; i++) - { - int currentCodeSizeOffset = CODESECTION_HEADER_PREFIX_SIZE + i * EofValidator.TWO_BYTE_LENGTH; // offset of pos'th code size - int codeSectionSize = GetUInt16(container, currentCodeSizeOffset); - - 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 += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * codeSections.Length; + sectionSizes.CodeSectionSize = codeHeaderSize; break; + case Separator.KIND_CONTAINER: if (sectionSizes.ContainerSectionSize != null) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Multiple container sections"); + 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 is not null) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Container section is out of order"); - return false; - } - - if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); return false; } - - var numberOfContainerSections = GetUInt16(container, pos); - sectionSizes.ContainerSectionSize = (ushort)(numberOfContainerSections * EofValidator.TWO_BYTE_LENGTH); - if (numberOfContainerSections is > (MAXIMUM_NUM_CONTAINER_SECTIONS + 1) or 0) + if (sectionSizes.DataSectionSize != null) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, container sections count must not exceed {MAXIMUM_NUM_CONTAINER_SECTIONS}"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Container section is out of order"); return false; } - - if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * numberOfContainerSections) + if (!TryParseContainerSection(ref pos, out containerSections, out ushort containerHeaderSize, container)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); return false; } - - containerSections = new int[numberOfContainerSections]; - int CONTAINER_SECTION_HEADER_PREFIX_SIZE = pos + EofValidator.TWO_BYTE_LENGTH; - for (ushort i = 0; i < containerSections.Length; i++) - { - int currentContainerSizeOffset = CONTAINER_SECTION_HEADER_PREFIX_SIZE + i * EofValidator.TWO_BYTE_LENGTH; // offset of pos'th code size - int containerSectionSize = GetUInt16(container, currentContainerSizeOffset); - - 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 += EofValidator.TWO_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH * containerSections.Length; + sectionSizes.ContainerSectionSize = containerHeaderSize; break; + case Separator.KIND_DATA: if (sectionSizes.DataSectionSize != null) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Multiple data sections"); + 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 fromated"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); return false; } - - if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + if (!TryParseDataSection(ref pos, out ushort dataSectionSize, container)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); return false; } - - sectionSizes.DataSectionSize = GetUInt16(container, pos); - - pos += EofValidator.TWO_BYTE_LENGTH; + sectionSizes.DataSectionSize = dataSectionSize; break; + case Separator.TERMINATOR: + // The terminator must be followed by at least one byte. if (container.Length < pos + EofValidator.ONE_BYTE_LENGTH) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); + 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"); + 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"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Code is not well formatted"); return false; } - var typeSectionSubHeader = new SectionHeader(pos, sectionSizes.TypeSectionSize.Value); - var codeSectionSubHeader = new CompoundSectionHeader(typeSectionSubHeader.EndOffset, codeSections); - CompoundSectionHeader? containerSectionSubHeader = containerSections is null ? null - : new CompoundSectionHeader(codeSectionSubHeader.EndOffset, containerSections); - var dataSectionSubHeader = new SectionHeader(containerSectionSubHeader?.EndOffset ?? codeSectionSubHeader.EndOffset, sectionSizes.DataSectionSize.Value); - - if (!validationStrategy.HasFlag(ValidationStrategy.AllowTrailingBytes) && dataSectionSubHeader.EndOffset < containerMemory.Length) + // 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 {dataSectionSubHeader.EndOffset}"); + 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)) - && dataSectionSubHeader.EndOffset > container.Length) + 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"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Container has truncated data where full data is required"); return false; } @@ -362,38 +237,280 @@ static ushort GetUInt16(ReadOnlySpan container, int offset) => { Version = VERSION, PrefixSize = pos, - TypeSection = typeSectionSubHeader, - CodeSections = codeSectionSubHeader, - ContainerSections = containerSectionSubHeader, - DataSection = dataSectionSubHeader, + TypeSection = typeSectionHeader, + CodeSections = codeSectionHeader, + ContainerSections = containerSectionHeader, + DataSection = dataSectionHeader, }; + return true; } - public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy validationStrategy, [NotNullWhen(true)] out EofContainer? eofContainer) + /// + /// 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(EofValidator.MAGIC)) + { + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Code doesn't start with magic byte sequence expected {EofValidator.MAGIC.ToHexString(true)} "); + return false; + } + if (container[EofValidator.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 + EofValidator.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 += EofValidator.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 + EofValidator.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 * EofValidator.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 + EofValidator.TWO_BYTE_LENGTH + (numberOfCodeSections * EofValidator.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 + EofValidator.TWO_BYTE_LENGTH; + for (int i = 0; i < codeSections.Length; i++) + { + int currentOffset = headerStart + (i * EofValidator.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 += EofValidator.TWO_BYTE_LENGTH + (numberOfCodeSections * EofValidator.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 + EofValidator.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 * EofValidator.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 + EofValidator.TWO_BYTE_LENGTH + (numberOfContainerSections * EofValidator.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 + EofValidator.TWO_BYTE_LENGTH; + for (int i = 0; i < containerSections.Length; i++) + { + int currentOffset = headerStart + (i * EofValidator.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 += EofValidator.TWO_BYTE_LENGTH + (numberOfContainerSections * EofValidator.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 + EofValidator.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 += EofValidator.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, EofValidator.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"); - eofContainer = null; return false; } - if (!ValidateBody(code.Span, header.Value, validationStrategy)) + // 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"); - eofContainer = null; 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"); - eofContainer = null; return false; } } @@ -401,63 +518,135 @@ public bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy val return true; } - private bool ValidateContainer(EofContainer eofContainer, ValidationStrategy validationStrategy) + /// + /// 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)) { - EofContainer targetContainer = target.container; - validationStrategy = target.strategy; - - QueueManager containerQueue = new(1 + (targetContainer.Header.ContainerSections?.Count ?? 0)); - containerQueue.Enqueue(0, validationStrategy); - - containerQueue.VisitedContainers[0] = GetValidation(validationStrategy); - - while (containerQueue.TryDequeue(out (int Index, ValidationStrategy Strategy) worklet)) + // Process the current container. If validation fails at any level, return false. + if (!ProcessContainer(target.container, target.strategy, containers)) { - if (worklet.Index != 0) - { - if (containerQueue.VisitedContainers[worklet.Index] != 0) - continue; + return false; + } + } - if (targetContainer.ContainerSections.Length < worklet.Index) - continue; + return true; + } - var section = worklet.Index - 1; - ReadOnlyMemory subsection = targetContainer.ContainerSections[section]; - if (!TryParseEofHeader(subsection, validationStrategy, out EofHeader? header)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Header invalid: section {section}"); - return false; - } - if (!ValidateBody(subsection.Span, header.Value, validationStrategy)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Body invalid: section {section}"); - return false; - } + /// + /// 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)); - if (validationStrategy.HasFlag(ValidationStrategy.Validate)) - { - containers.Enqueue((new EofContainer(subsection, header.Value), worklet.Strategy)); - } - } - else + // 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 (!ValidateCodeSections(targetContainer, worklet.Strategy, in containerQueue)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code sections invalid"); - return false; - } + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Code sections invalid"); + return false; } - containerQueue.MarkVisited(worklet.Index, GetVisited(worklet.Strategy)); } - if (!containerQueue.IsAllVisited()) + else { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Not all containers visited"); - return false; + // 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.IsAllVisited()) + { + 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; @@ -479,7 +668,7 @@ private static ValidationStrategy GetValidation(ValidationStrategy validationStr : ValidationStrategy.None; } - private bool ValidateBody(ReadOnlySpan container, EofHeader header, ValidationStrategy strategy) + private static bool ValidateBody(in EofHeader header, ValidationStrategy strategy, ReadOnlySpan container) { int startOffset = header.TypeSection.Start; int endOffset = header.DataSection.Start; @@ -547,7 +736,7 @@ private bool ValidateBody(ReadOnlySpan container, EofHeader header, Valida return true; } - private bool ValidateCodeSections(EofContainer eofContainer, ValidationStrategy strategy, in QueueManager containerQueue) + private static bool ValidateCodeSections(in EofContainer eofContainer, ValidationStrategy strategy, in QueueManager containerQueue) { QueueManager sectionQueue = new(eofContainer.Header.CodeSections.Count); @@ -567,7 +756,7 @@ private bool ValidateCodeSections(EofContainer eofContainer, ValidationStrategy return sectionQueue.IsAllVisited(); } - private bool ValidateTypeSection(ReadOnlySpan types) + private static bool ValidateTypeSection(ReadOnlySpan types) { if (types[INPUTS_OFFSET] != 0 || types[OUTPUTS_OFFSET] != NON_RETURNING) { @@ -608,7 +797,7 @@ private bool ValidateTypeSection(ReadOnlySpan types) return true; } - private bool ValidateInstructions(EofContainer eofContainer, int sectionId, ValidationStrategy strategy, in QueueManager sectionsWorklist, in QueueManager containersWorklist) + private static bool ValidateInstructions(in EofContainer eofContainer, int sectionId, ValidationStrategy strategy, in QueueManager sectionsWorklist, in QueueManager containersWorklist) { ReadOnlySpan code = eofContainer.CodeSections[sectionId].Span; @@ -948,22 +1137,21 @@ private bool ValidateInstructions(EofContainer eofContainer, int sectionId, Vali ArrayPool.Shared.Return(jumpDestinationsArray); } } - public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in ReadOnlySpan typesection) + + public static bool ValidateStackState(int sectionId, ReadOnlySpan code, ReadOnlySpan typeSection) { StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); - recordedStackHeight.Fill(new StackBounds()); + Array.Fill(recordedStackHeight, new StackBounds()); try { - ushort suggestedMaxHeight = typesection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + EofValidator.TWO_BYTE_LENGTH, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + ushort suggestedMaxHeight = typeSection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + EofValidator.TWO_BYTE_LENGTH, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); - var currrentSectionOutputs = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - short peakStackHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + ushort currentSectionOutputs = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + short peakStackHeight = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; var unreachedBytes = code.Length; var isTargetSectionNonReturning = false; - - var targetMaxStackHeight = 0; var programCounter = 0; recordedStackHeight[0].Max = peakStackHeight; recordedStackHeight[0].Min = peakStackHeight; @@ -985,12 +1173,12 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea { case Instruction.CALLF or Instruction.JUMPF: ushort targetSectionId = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); - inputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + inputs = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - outputs = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - isTargetSectionNonReturning = typesection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; + outputs = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + isTargetSectionNonReturning = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); - targetMaxStackHeight = typesection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + int targetMaxStackHeight = typeSection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) { @@ -998,9 +1186,9 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea return false; } - if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currrentSectionOutputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) + if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currentSectionOutputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currrentSectionOutputs + inputs - outputs} but found {currentStackBounds.Max}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currentSectionOutputs + inputs - outputs} but found {currentStackBounds.Max}"); return false; } break; @@ -1038,7 +1226,7 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea { case Instruction.RETF: { - var expectedHeight = typesection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + var 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}"); @@ -1148,5 +1336,62 @@ public bool ValidateStackState(int sectionId, in ReadOnlySpan code, in Rea ArrayPool.Shared.Return(recordedStackHeight); } } + + 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 IsAllVisited() => VisitedContainers.All(x => x != 0); + } + + [StructLayout(LayoutKind.Sequential)] + private struct StackBounds() + { + public short Max = -1; + public short Min = 1023; + + public void Combine(StackBounds other) + { + Max = Math.Max(Max, other.Max); + Min = Math.Min(Min, other.Min); + } + + public readonly bool BoundsEqual() => Max == Min; + + public static bool operator ==(StackBounds left, StackBounds right) => left.Max == right.Max && right.Min == left.Min; + public static bool operator !=(StackBounds left, 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; + } + + 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 + } } diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs index 7254ced54ea..3aa3a4fadb4 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/IEofVersionHandler.cs @@ -8,5 +8,5 @@ namespace Nethermind.Evm.EvmObjectFormat; interface IEofVersionHandler { bool TryParseEofHeader(ReadOnlyMemory code, ValidationStrategy strategy, [NotNullWhen(true)] out EofHeader? header); - bool TryGetEofContainer(ReadOnlyMemory code, ValidationStrategy strategy, [NotNullWhen(true)] out EofContainer? header); + bool TryGetEofContainer(ValidationStrategy strategy, [NotNullWhen(true)] out EofContainer? header, ReadOnlyMemory code); } From 36fb787aa5ecae66f6365a6d0e24af71816e95c1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 6 Feb 2025 03:53:28 +0000 Subject: [PATCH 252/255] Tidy up --- .../EvmObjectFormat/Handlers/EofV1.cs | 885 ++++++++++++------ 1 file changed, 618 insertions(+), 267 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 97cb32599a2..a9fb00d9d98 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -25,11 +25,11 @@ internal class Eof1 : IEofVersionHandler 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 = EofValidator.VERSION_OFFSET + internal const byte MINIMUM_HEADER_SIZE = VERSION_OFFSET + MINIMUM_HEADER_SECTION_SIZE - + MINIMUM_HEADER_SECTION_SIZE + EofValidator.TWO_BYTE_LENGTH + + MINIMUM_HEADER_SECTION_SIZE + TWO_BYTE_LENGTH + MINIMUM_HEADER_SECTION_SIZE - + EofValidator.ONE_BYTE_LENGTH; + + 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 @@ -85,7 +85,7 @@ public bool TryParseEofHeader(ReadOnlyMemory containerMemory, ValidationSt } // The current read position; after the version byte. - int pos = EofValidator.VERSION_OFFSET + 1; + int pos = VERSION_OFFSET + 1; // Holds header size information for each section. Sizes sectionSizes = new(); @@ -183,7 +183,7 @@ public bool TryParseEofHeader(ReadOnlyMemory containerMemory, ValidationSt case Separator.TERMINATOR: // The terminator must be followed by at least one byte. - if (container.Length < pos + EofValidator.ONE_BYTE_LENGTH) + if (container.Length < pos + ONE_BYTE_LENGTH) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); @@ -265,13 +265,13 @@ private static bool ValidateBasicConstraints(ReadOnlySpan container) Logger.Trace($"EOF: Eof{VERSION}, Code is larger than allowed maximum size of {MAXIMUM_SIZE}"); return false; } - if (!container.StartsWith(EofValidator.MAGIC)) + if (!container.StartsWith(MAGIC)) { if (Logger.IsTrace) - Logger.Trace($"EOF: Eof{VERSION}, Code doesn't start with magic byte sequence expected {EofValidator.MAGIC.ToHexString(true)} "); + Logger.Trace($"EOF: Eof{VERSION}, Code doesn't start with magic byte sequence expected {MAGIC.ToHexString(true)} "); return false; } - if (container[EofValidator.VERSION_OFFSET] != VERSION) + if (container[VERSION_OFFSET] != VERSION) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is not Eof version {VERSION}"); @@ -293,7 +293,7 @@ private static bool TryParseTypeSection(ref int pos, out ushort typeSectionSize, { typeSectionSize = 0; // Ensure enough bytes are available to read the type section size. - if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + if (container.Length < pos + TWO_BYTE_LENGTH) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); @@ -306,7 +306,7 @@ private static bool TryParseTypeSection(ref int pos, out ushort typeSectionSize, Logger.Trace($"EOF: Eof{VERSION}, TypeSection Size must be at least {MINIMUM_TYPESECTION_SIZE}, but found {typeSectionSize}"); return false; } - pos += EofValidator.TWO_BYTE_LENGTH; + pos += TWO_BYTE_LENGTH; return true; } @@ -329,7 +329,7 @@ private static bool TryParseCodeSection(ref int pos, out int[]? codeSections, ou codeSections = null; headerSize = 0; // Must have enough bytes to read the count of code sections. - if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + if (container.Length < pos + TWO_BYTE_LENGTH) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); @@ -337,7 +337,7 @@ private static bool TryParseCodeSection(ref int pos, out int[]? codeSections, ou } ushort numberOfCodeSections = GetUInt16(pos, container); - headerSize = (ushort)(numberOfCodeSections * EofValidator.TWO_BYTE_LENGTH); + headerSize = (ushort)(numberOfCodeSections * TWO_BYTE_LENGTH); if (numberOfCodeSections > MAXIMUM_NUM_CODE_SECTIONS) { @@ -346,7 +346,7 @@ private static bool TryParseCodeSection(ref int pos, out int[]? codeSections, ou return false; } - int requiredLength = pos + EofValidator.TWO_BYTE_LENGTH + (numberOfCodeSections * EofValidator.TWO_BYTE_LENGTH); + int requiredLength = pos + TWO_BYTE_LENGTH + (numberOfCodeSections * TWO_BYTE_LENGTH); if (container.Length < requiredLength) { if (Logger.IsTrace) @@ -355,10 +355,10 @@ private static bool TryParseCodeSection(ref int pos, out int[]? codeSections, ou } codeSections = new int[numberOfCodeSections]; - int headerStart = pos + EofValidator.TWO_BYTE_LENGTH; + int headerStart = pos + TWO_BYTE_LENGTH; for (int i = 0; i < codeSections.Length; i++) { - int currentOffset = headerStart + (i * EofValidator.TWO_BYTE_LENGTH); + int currentOffset = headerStart + (i * TWO_BYTE_LENGTH); int codeSectionSize = GetUInt16(currentOffset, container); if (codeSectionSize == 0) { @@ -368,7 +368,7 @@ private static bool TryParseCodeSection(ref int pos, out int[]? codeSections, ou } codeSections[i] = codeSectionSize; } - pos += EofValidator.TWO_BYTE_LENGTH + (numberOfCodeSections * EofValidator.TWO_BYTE_LENGTH); + pos += TWO_BYTE_LENGTH + (numberOfCodeSections * TWO_BYTE_LENGTH); return true; } @@ -390,7 +390,7 @@ private static bool TryParseContainerSection(ref int pos, out int[]? containerSe { containerSections = null; headerSize = 0; - if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + if (container.Length < pos + TWO_BYTE_LENGTH) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code is too small to be valid code"); @@ -398,7 +398,7 @@ private static bool TryParseContainerSection(ref int pos, out int[]? containerSe } ushort numberOfContainerSections = GetUInt16(pos, container); - headerSize = (ushort)(numberOfContainerSections * EofValidator.TWO_BYTE_LENGTH); + 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)) @@ -408,7 +408,7 @@ private static bool TryParseContainerSection(ref int pos, out int[]? containerSe return false; } - int requiredLength = pos + EofValidator.TWO_BYTE_LENGTH + (numberOfContainerSections * EofValidator.TWO_BYTE_LENGTH); + int requiredLength = pos + TWO_BYTE_LENGTH + (numberOfContainerSections * TWO_BYTE_LENGTH); if (container.Length < requiredLength) { if (Logger.IsTrace) @@ -417,10 +417,10 @@ private static bool TryParseContainerSection(ref int pos, out int[]? containerSe } containerSections = new int[numberOfContainerSections]; - int headerStart = pos + EofValidator.TWO_BYTE_LENGTH; + int headerStart = pos + TWO_BYTE_LENGTH; for (int i = 0; i < containerSections.Length; i++) { - int currentOffset = headerStart + (i * EofValidator.TWO_BYTE_LENGTH); + int currentOffset = headerStart + (i * TWO_BYTE_LENGTH); int containerSectionSize = GetUInt16(currentOffset, container); if (containerSectionSize == 0) { @@ -430,7 +430,7 @@ private static bool TryParseContainerSection(ref int pos, out int[]? containerSe } containerSections[i] = containerSectionSize; } - pos += EofValidator.TWO_BYTE_LENGTH + (numberOfContainerSections * EofValidator.TWO_BYTE_LENGTH); + pos += TWO_BYTE_LENGTH + (numberOfContainerSections * TWO_BYTE_LENGTH); return true; } @@ -448,14 +448,14 @@ private static bool TryParseContainerSection(ref int pos, out int[]? containerSe private static bool TryParseDataSection(ref int pos, out ushort dataSectionSize, ReadOnlySpan container) { dataSectionSize = 0; - if (container.Length < pos + EofValidator.TWO_BYTE_LENGTH) + 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 += EofValidator.TWO_BYTE_LENGTH; + pos += TWO_BYTE_LENGTH; return true; } @@ -467,7 +467,7 @@ private static bool TryParseDataSection(ref int pos, out ushort dataSectionSize, /// The 16‐bit unsigned integer value. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort GetUInt16(int offset, ReadOnlySpan container) => - container.Slice(offset, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + container.Slice(offset, TWO_BYTE_LENGTH).ReadEthUInt16(); /// /// Attempts to create an from the provided raw code data. @@ -668,186 +668,365 @@ private static ValidationStrategy GetValidation(ValidationStrategy validationStr : 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; - int calculatedCodeLength = - header.TypeSection.Size - + header.CodeSections.Size - + (header.ContainerSections?.Size ?? 0); - CompoundSectionHeader codeSections = header.CodeSections; + // Ensure the DataSection starts within the container. if (endOffset > container.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + 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..]; - SectionHeader typeSection = header.TypeSection; - (int typeSectionStart, int typeSectionSize) = (typeSection.Start, typeSection.Size); + // 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. if (header.ContainerSections?.Count > MAXIMUM_NUM_CONTAINER_SECTIONS + 1) { - // move this check where `header.ExtraContainers.Count` is parsed - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, InitCode Containers count must be less than {MAXIMUM_NUM_CONTAINER_SECTIONS} but found {header.ContainerSections?.Count}"); + // 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; } - if (contractBody.Length != calculatedCodeLength) + // 3. Validate the DataSection against the provided strategy. + if (!ValidateDataSection(header, strategy, dataBody)) { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, SectionSizes indicated in bundled header are incorrect, or ContainerCode is incomplete"); return false; } - if (strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size > dataBody.Length) + // 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}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, CodeSection count ({codeSections.Count}) is zero or contains an empty section."); return false; } - if (!strategy.HasFlag(ValidationStrategy.AllowTrailingBytes) && strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size != dataBody.Length) + // 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}, DataSectionSize indicated in bundled header are incorrect, or DataSection is wrong"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, CodeSections count ({codeSections.Count}) does not match expected type count ({expectedTypeCount})."); return false; } - if (codeSections.Count == 0 || codeSections.SubSectionsSizes.Any(size => size == 0)) + // 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}, CodeSection size must follow a CodeSection, CodeSection length was {codeSections.Count}"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Invalid TypeSection content."); return false; } - if (codeSections.Count != typeSectionSize / MINIMUM_TYPESECTION_SIZE) + 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, the DataSection size must be less than or equal to the data available. + if (strategy.HasFlag(ValidationStrategy.ValidateFullBody) && header.DataSection.Size > dataBody.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Code Sections count must match TypeSection count, CodeSection count was {codeSections.Count}, expected {typeSectionSize / MINIMUM_TYPESECTION_SIZE}"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, DataSection size ({header.DataSection.Size}) exceeds available data ({dataBody.Length})."); return false; } - ReadOnlySpan typeSectionBytes = container.Slice(typeSectionStart, typeSectionSize); - if (!ValidateTypeSection(typeSectionBytes)) + // When trailing bytes are not allowed, the DataSection size must exactly match the available data. + if (!strategy.HasFlag(ValidationStrategy.AllowTrailingBytes) + && strategy.HasFlag(ValidationStrategy.ValidateFullBody) + && header.DataSection.Size != dataBody.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, invalid TypeSection found"); + 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. + return 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.IsAllVisited(); } + /// + /// 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"); + 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}"); + 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) { - var inputCount = types[offset + INPUTS_OFFSET]; - var outputCount = types[offset + OUTPUTS_OFFSET]; - ushort maxStackHeight = types.Slice(offset + MAX_STACK_HEIGHT_OFFSET, MAX_STACK_HEIGHT_LENGTH).ReadEthUInt16(); + // Extract the current entry. + ReadOnlySpan entry = types.Slice(offset, MINIMUM_TYPESECTION_SIZE); - if (inputCount > INPUTS_MAX) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Too many inputs"); + // Validate the individual type entry. + if (!ValidateTypeSectionEntry(entry)) return false; - } + } - if (outputCount > OUTPUTS_MAX && outputCount != NON_RETURNING) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Too many outputs"); - return false; - } + return true; + } - if (maxStackHeight > MAX_STACK_HEIGHT) - { - if (Logger.IsTrace) Logger.Trace("EOF: Eof{VERSION}, Stack depth too high"); - return false; - } + /// + /// 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; } - - private static bool ValidateInstructions(in EofContainer eofContainer, int sectionId, ValidationStrategy strategy, in QueueManager sectionsWorklist, in QueueManager containersWorklist) + /// + /// 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"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, CodeSection {sectionId} is too short to be valid"); return false; } - var length = code.Length / BYTE_BIT_COUNT + 1; - byte[] invalidJmpLocationArray = ArrayPool.Shared.Rent(length); - byte[] jumpDestinationsArray = ArrayPool.Shared.Rent(length); + // 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 { - // ArrayPool may return a larger array than requested, so we need to slice it to the actual length - Span invalidJumpDestinations = invalidJmpLocationArray.AsSpan(0, length); - Span jumpDestinations = jumpDestinationsArray.AsSpan(0, length); - // ArrayPool may return a larger array than requested, so we need to slice it to the actual length + // 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; - var isCurrentSectionNonReturning = currentTypeSection[OUTPUTS_OFFSET] == 0x80; + bool isCurrentSectionNonReturning = currentTypeSection[OUTPUTS_OFFSET] == 0x80; + // If the section is non–returning, an exit is already implied. bool hasRequiredSectionExit = isCurrentSectionNonReturning; - int position; + int position = 0; Instruction opcode = Instruction.STOP; - for (position = 0; position < code.Length;) + + 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}"); + 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"); + 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"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); return false; } else @@ -856,184 +1035,87 @@ private static bool ValidateInstructions(in EofContainer eofContainer, int secti } } } - else if (opcode is Instruction.RETURNCONTRACT) + else if (opcode == Instruction.RETURNCONTRACT) { - if (strategy.HasFlag(ValidationStrategy.ValidateRuntimeMode)) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); + // Validate the RETURNCONTRACT branch. + if (!ValidateReturnContract(ref nextPosition, strategy, containersWorklist, eofContainer, code, invalidJumpDestinations)) return false; - } - else - { - if (containersWorklist.VisitedContainers[0] == ValidationStrategy.ValidateRuntimeMode) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {opcode} opcode"); - return false; - } - else - { - containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateInitcodeMode; - } - } - - if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT} Argument underflow"); - return false; - } - - ushort runtimeContainerId = code[nextPosition]; - if (eofContainer.Header.ContainerSections is null || runtimeContainerId >= eofContainer.Header.ContainerSections?.Count) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RETURNCONTRACT}'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.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); - return false; - } - - containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode | ValidationStrategy.ValidateFullBody); - - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.RJUMP or Instruction.RJUMPI) { - if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); + // Validate relative jump instructions. + if (!ValidateRelativeJump(ref nextPosition, opcode, code, jumpDestinations, invalidJumpDestinations)) return false; - } - - short offset = code.Slice(nextPosition, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); - int rjumpDest = offset + EofValidator.TWO_BYTE_LENGTH + nextPosition; - - if (rjumpDest < 0 || rjumpDest >= code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Destination outside of Code bounds"); - return false; - } - - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDestinations, ref rjumpDest); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } - else if (opcode is Instruction.JUMPF) + else if (opcode == Instruction.JUMPF) { + // A JUMPF always implies a required section exit. hasRequiredSectionExit = true; - if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) + if (nextPosition + TWO_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} Argument underflow"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} Argument underflow"); return false; } - var targetSectionId = code.Slice(nextPosition, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + 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"); + 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] == 0x80; + byte currentSectionOutputCount = currentTypeSection[OUTPUTS_OFFSET]; - var targetSectionOutputCount = targetTypeSection[OUTPUTS_OFFSET]; - var isTargetSectionNonReturning = targetTypeSection[OUTPUTS_OFFSET] == 0x80; - var 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"); + 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"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, {Instruction.JUMPF} from non-returning must target non-returning"); return false; } sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); + BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) { - if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) + if (nextPosition + ONE_BYTE_LENGTH > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); return false; } - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); + BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } - else if (opcode is Instruction.RJUMPV) + else if (opcode == Instruction.RJUMPV) { - if (nextPosition + EofValidator.ONE_BYTE_LENGTH + EofValidator.TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Argument underflow"); + // Validate the relative jump table. + if (!ValidateRelativeJumpV(ref nextPosition, code, jumpDestinations, invalidJumpDestinations)) return false; - } - - var count = (ushort)(code[nextPosition] + 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 (nextPosition + EofValidator.ONE_BYTE_LENGTH + count * EofValidator.TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} jumpTable underflow"); - return false; - } - - int immediateValueSize = EofValidator.ONE_BYTE_LENGTH + count * EofValidator.TWO_BYTE_LENGTH; - for (var j = 0; j < count; j++) - { - var offset = code.Slice(nextPosition + EofValidator.ONE_BYTE_LENGTH + j * EofValidator.TWO_BYTE_LENGTH, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); - var rjumpDest = offset + immediateValueSize + nextPosition; - if (rjumpDest < 0 || rjumpDest >= code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.RJUMPV} Destination outside of Code bounds"); - return false; - } - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, jumpDestinations, ref rjumpDest); - } - - BitmapHelper.HandleNumbits(immediateValueSize, invalidJumpDestinations, ref nextPosition); } - else if (opcode is Instruction.CALLF) + else if (opcode == Instruction.CALLF) { - if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} Argument underflow"); - return false; - } - - ushort targetSectionId = code.Slice(nextPosition, EofValidator.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; - - var targetSectionOutputCount = targetTypeSection[OUTPUTS_OFFSET]; - - if (targetSectionOutputCount == 0x80) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} into non-returning function"); + // Validate the CALLF instruction. + if (!ValidateCallF(ref nextPosition, eofContainer, sectionsWorklist, strategy, code, invalidJumpDestinations)) return false; - } - - sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } - else if (opcode is Instruction.RETF) + 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) { @@ -1042,91 +1124,65 @@ private static bool ValidateInstructions(in EofContainer eofContainer, int secti return false; } } - else if (opcode is Instruction.DATALOADN) + else if (opcode == Instruction.DATALOADN) { - if (nextPosition + EofValidator.TWO_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.DATALOADN} Argument underflow"); - return false; - } - - ushort dataSectionOffset = code.Slice(nextPosition, EofValidator.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}"); + // Validate that the data offset is within the data section bounds. + if (!ValidateDataLoadN(ref nextPosition, eofContainer, code, invalidJumpDestinations)) return false; - } - BitmapHelper.HandleNumbits(EofValidator.TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } - else if (opcode is Instruction.EOFCREATE) + else if (opcode == Instruction.EOFCREATE) { - if (nextPosition + EofValidator.ONE_BYTE_LENGTH > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE} Argument underflow"); - return false; - } - - int initCodeSectionId = code[nextPosition]; - - if (eofContainer.Header.ContainerSections is null || initCodeSectionId >= eofContainer.Header.ContainerSections?.Count) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {Instruction.EOFCREATE}'s immediate must falls within the Containers' range available, i.e: {eofContainer.Header.CodeSections.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"); + // Validate the EOFCREATE instruction. + if (!ValidateEofCreate(ref nextPosition, eofContainer, containersWorklist, code, invalidJumpDestinations)) return false; - } - - containersWorklist.Enqueue(initCodeSectionId + 1, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateFullBody); - - BitmapHelper.HandleNumbits(EofValidator.ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } - else if (opcode is >= Instruction.PUSH0 and <= Instruction.PUSH32) + 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"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} PC Reached out of bounds"); return false; } BitmapHelper.HandleNumbits(pushDataLength, invalidJumpDestinations, ref nextPosition); } + position = nextPosition; } if (position > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); + 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"); + 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"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Code section {sectionId} is returning and does not have a RETF or JUMPF"); return false; } - var result = BitmapHelper.CheckCollision(invalidJumpDestinations, jumpDestinations); - if (result) + if (BitmapHelper.CheckCollision(invalidJumpDestinations, jumpDestinations)) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Invalid Jump destination {result}"); + 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"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Invalid Stack state"); return false; } return true; @@ -1138,6 +1194,301 @@ private static bool ValidateInstructions(in EofContainer eofContainer, int secti } } + /// + /// Validates the RETURNCONTRACT 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 RETURNCONTRACT branch is valid; otherwise, false. + private static bool ValidateReturnContract( + 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.RETURNCONTRACT} opcode"); + return false; + } + else + { + if (containersWorklist.VisitedContainers[0] == ValidationStrategy.ValidateRuntimeMode) + { + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, CodeSection cannot contain {Instruction.RETURNCONTRACT} 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.RETURNCONTRACT} 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.RETURNCONTRACT}'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.RETURNCONTRACT}'s target container can only be a runtime mode bytecode"); + return false; + } + + containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode | ValidationStrategy.ValidateFullBody); + BitmapHelper.HandleNumbits(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.HandleNumbits(ONE_BYTE_LENGTH, jumpDestinations, ref rjumpDest); + BitmapHelper.HandleNumbits(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.HandleNumbits(ONE_BYTE_LENGTH, jumpDestinations, ref rjumpDest); + } + + BitmapHelper.HandleNumbits(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 == 0x80) + { + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, {Instruction.CALLF} into non-returning function"); + return false; + } + + sectionsWorklist.Enqueue(targetSectionId, strategy); + BitmapHelper.HandleNumbits(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.HandleNumbits(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.CodeSections.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.HandleNumbits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref pos); + return true; + } + public static bool ValidateStackState(int sectionId, ReadOnlySpan code, ReadOnlySpan typeSection) { StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); @@ -1145,7 +1496,7 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re try { - ushort suggestedMaxHeight = typeSection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + EofValidator.TWO_BYTE_LENGTH, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + ushort suggestedMaxHeight = typeSection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); ushort currentSectionOutputs = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; short peakStackHeight = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; @@ -1178,7 +1529,7 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re outputs = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; isTargetSectionNonReturning = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); - int targetMaxStackHeight = typeSection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, EofValidator.TWO_BYTE_LENGTH).ReadEthUInt16(); + int targetMaxStackHeight = typeSection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) { @@ -1258,11 +1609,11 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re case Instruction.RJUMPV: { var count = code[posPostInstruction] + 1; - immediates = (ushort)(count * EofValidator.TWO_BYTE_LENGTH + EofValidator.ONE_BYTE_LENGTH); + immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); for (short j = 0; j < count; j++) { - int case_v = posPostInstruction + EofValidator.ONE_BYTE_LENGTH + j * EofValidator.TWO_BYTE_LENGTH; - int offset = code.Slice(case_v, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); + int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; + int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); var jumpDestination = posPostInstruction + immediates.Value + offset; if (jumpDestination > programCounter) recordedStackHeight[jumpDestination].Combine(currentStackBounds); From a0fac5aaea4432a182072b5ee556a551ca9ddc27 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 6 Feb 2025 04:53:19 +0000 Subject: [PATCH 253/255] tidy up --- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 2 +- src/Nethermind/Nethermind.Evm/BitmapHelper.cs | 60 +++---- .../Nethermind.Evm/CodeInfoFactory.cs | 2 +- .../EvmObjectFormat/EofValidationStrategy.cs | 2 +- .../EvmObjectFormat/Handlers/EofV1.cs | 154 ++++++++---------- .../Instructions/EvmInstructions.Eof.cs | 49 +++--- 6 files changed, 133 insertions(+), 136 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 77c9fbd8499..ae92fd57e8b 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -340,7 +340,7 @@ public static IEnumerable ConvertToEofTests(string json) vector.Code = Bytes.FromHexString(vectorJson.Code); vector.ContainerKind = ("INITCODE".Equals(vectorJson.ContainerKind) - ? ValidationStrategy.ValidateInitcodeMode + ? ValidationStrategy.ValidateInitCodeMode : ValidationStrategy.ValidateRuntimeMode) | ValidationStrategy.ValidateFullBody; diff --git a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs index 54758ff59f3..5d6be0f7b09 100644 --- a/src/Nethermind/Nethermind.Evm/BitmapHelper.cs +++ b/src/Nethermind/Nethermind.Evm/BitmapHelper.cs @@ -29,7 +29,7 @@ public static byte[] CreateCodeBitmap(ReadOnlySpan code, bool isEof = fals // The bitmap is 4 bytes longer than necessary, in case the code // ends with a PUSH32, the algorithm will push zeroes onto the // bitvector outside the bounds of the actual code. - byte[] bitvec = new byte[(code.Length / 8) + 1 + 4]; + byte[] bitVector = new byte[(code.Length / 8) + 1 + 4]; for (int pc = 0; pc < code.Length;) { @@ -44,80 +44,80 @@ public static byte[] CreateCodeBitmap(ReadOnlySpan code, bool isEof = fals if (numbits == 0) continue; - HandleNumbits(numbits, bitvec, ref pc); + FlagMultipleBits(numbits, bitVector, ref pc); } - return bitvec; + return bitVector; } - public static void HandleNumbits(int numbits, Span bitvec, scoped ref int pc) + public static void FlagMultipleBits(int bitCount, Span bitVector, scoped ref int pc) { - if (numbits == 0) return; + if (bitCount == 0) return; - if (numbits >= 8) + if (bitCount >= 8) { - for (; numbits >= 16; numbits -= 16) + for (; bitCount >= 16; bitCount -= 16) { - bitvec.Set16(pc); + bitVector.Set16(pc); pc += 16; } - for (; numbits >= 8; numbits -= 8) + for (; bitCount >= 8; bitCount -= 8) { - bitvec.Set8(pc); + bitVector.Set8(pc); pc += 8; } } - if (numbits > 1) + if (bitCount > 1) { - bitvec.SetN(pc, _lookup[numbits]); - pc += numbits; + bitVector.SetN(pc, _lookup[bitCount]); + pc += bitCount; } else { - bitvec.Set1(pc); - pc += numbits; + bitVector.Set1(pc); + pc += bitCount; } } /// /// Checks if the position is in a code segment. /// - public static bool IsCodeSegment(Span bitvec, int pos) + public static bool IsCodeSegment(Span bitVector, int pos) { - return (bitvec[pos / 8] & (0x80 >> (pos % 8))) == 0; + return (bitVector[pos / 8] & (0x80 >> (pos % 8))) == 0; } - private static void Set1(this Span bitvec, int pos) + private static void Set1(this Span bitVector, int pos) { - bitvec[pos / 8] |= (byte)(1 << (pos % 8)); + bitVector[pos / 8] |= (byte)(1 << (pos % 8)); } - private static void SetN(this Span bitvec, int pos, ushort flag) + private static void SetN(this Span bitVector, int pos, ushort flag) { ushort a = (ushort)(flag << (pos % 8)); - bitvec[pos / 8] |= (byte)a; + bitVector[pos / 8] |= (byte)a; byte b = (byte)(a >> 8); if (b != 0) { - // If the bit-setting affects the neighbouring byte, we can assign - no need to OR it, + // If the bit-setting affects the neighboring byte, we can assign - no need to OR it, // since it's the first write to that byte - bitvec[pos / 8 + 1] = b; + bitVector[pos / 8 + 1] = b; } } - private static void Set8(this Span bitvec, int pos) + private static void Set8(this Span bitVector, int pos) { byte a = (byte)(0xFF << (pos % 8)); - bitvec[pos / 8] |= a; - bitvec[pos / 8 + 1] = (byte)~a; + bitVector[pos / 8] |= a; + bitVector[pos / 8 + 1] = (byte)~a; } - private static void Set16(this Span bitvec, int pos) + private static void Set16(this Span bitVector, int pos) { byte a = (byte)(0xFF << (pos % 8)); - bitvec[pos / 8] |= a; - bitvec[pos / 8 + 1] = 0xFF; - bitvec[pos / 8 + 2] = (byte)~a; + bitVector[pos / 8] |= a; + bitVector[pos / 8 + 1] = 0xFF; + bitVector[pos / 8 + 2] = (byte)~a; } public static bool CheckCollision(ReadOnlySpan codeSegments, ReadOnlySpan jumpmask) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs index 2d3b48a9366..79693fb7365 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoFactory.cs @@ -28,7 +28,7 @@ public static bool CreateInitCodeInfo(Memory data, IReleaseSpec spec, out extraCallData = default; if (spec.IsEofEnabled && data.Span.StartsWith(EofValidator.MAGIC)) { - if (EofValidator.IsValidEof(data, ValidationStrategy.ValidateInitcodeMode | ValidationStrategy.ValidateFullBody | ValidationStrategy.AllowTrailingBytes, out EofContainer? eofContainer)) + if (EofValidator.IsValidEof(data, ValidationStrategy.ValidateInitCodeMode | ValidationStrategy.ValidateFullBody | ValidationStrategy.AllowTrailingBytes, out EofContainer? eofContainer)) { int containerSize = eofContainer.Value.Header.DataSection.EndOffset; extraCallData = data[containerSize..]; diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs index 4531eea20d7..1d2c407ccc2 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/EofValidationStrategy.cs @@ -8,7 +8,7 @@ public enum ValidationStrategy : byte None = 0, Validate = 1, ValidateFullBody = Validate | 2, - ValidateInitcodeMode = Validate | 4, + ValidateInitCodeMode = Validate | 4, ValidateRuntimeMode = Validate | 8, AllowTrailingBytes = Validate | 16, ExractHeader = 32, diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index a9fb00d9d98..d8ac4d1dd72 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -31,8 +31,8 @@ internal class Eof1 : IEofVersionHandler + 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 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; @@ -48,7 +48,7 @@ internal class Eof1 : IEofVersionHandler internal const ushort MINIMUM_NUM_CODE_SECTIONS = 1; internal const ushort MAXIMUM_NUM_CODE_SECTIONS = 1024; internal const ushort MAXIMUM_NUM_CONTAINER_SECTIONS = 0x00FF; - internal const ushort RETURN_STACK_MAX_HEIGHT = MAXIMUM_NUM_CODE_SECTIONS; // the size in the type sectionn allocated to each function section + 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 @@ -652,21 +652,15 @@ private bool ProcessContainerSection(in EofContainer targetContainer, int workle return true; } - private static ValidationStrategy GetVisited(ValidationStrategy validationStrategy) - { - return validationStrategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) - ? ValidationStrategy.ValidateInitcodeMode + private static ValidationStrategy GetVisited(ValidationStrategy validationStrategy) => validationStrategy.HasFlag(ValidationStrategy.ValidateInitCodeMode) + ? ValidationStrategy.ValidateInitCodeMode : ValidationStrategy.ValidateRuntimeMode; - } - private static ValidationStrategy GetValidation(ValidationStrategy validationStrategy) - { - return validationStrategy.HasFlag(ValidationStrategy.ValidateInitcodeMode) - ? ValidationStrategy.ValidateInitcodeMode + 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) @@ -808,11 +802,9 @@ private static bool ValidateDataSection(in EofHeader header, ValidationStrategy /// /// true if the code sections are valid; otherwise, false. /// - private static bool ValidateCodeSectionsNonZero(CompoundSectionHeader codeSections) - { + private static bool ValidateCodeSectionsNonZero(CompoundSectionHeader codeSections) => // The code sections must contain at least one subsection and none may have a size of zero. - return codeSections.Count > 0 && !codeSections.SubSectionsSizes.Any(size => size == 0); - } + codeSections.Count > 0 && !codeSections.SubSectionsSizes.Any(size => size == 0); /// /// Validates the instructions in all code sections of the provided EOF container. @@ -957,7 +949,7 @@ private static bool ValidateTypeSectionEntry(ReadOnlySpan entry) /// /// 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). + /// 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. @@ -1013,8 +1005,8 @@ private static bool ValidateInstructions( } else if (opcode is Instruction.RETURN or Instruction.STOP) { - // RETURN/STOP are disallowed in initcode mode. - if (strategy.HasFlag(ValidationStrategy.ValidateInitcodeMode)) + // RETURN/STOP are disallowed in InitCode mode. + if (strategy.HasFlag(ValidationStrategy.ValidateInitCodeMode)) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, CodeSection contains {opcode} opcode"); @@ -1022,8 +1014,8 @@ private static bool ValidateInstructions( } else { - // If the container has already been marked for initcode mode, RETURN/STOP is not allowed. - if (containersWorklist.VisitedContainers[0] == ValidationStrategy.ValidateInitcodeMode) + // 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"); @@ -1089,7 +1081,7 @@ private static bool ValidateInstructions( } sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); + BitmapHelper.FlagMultipleBits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode is Instruction.DUPN or Instruction.SWAPN or Instruction.EXCHANGE) { @@ -1099,7 +1091,7 @@ private static bool ValidateInstructions( Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Argument underflow"); return false; } - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); + BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref nextPosition); } else if (opcode == Instruction.RJUMPV) { @@ -1145,7 +1137,7 @@ private static bool ValidateInstructions( Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} PC Reached out of bounds"); return false; } - BitmapHelper.HandleNumbits(pushDataLength, invalidJumpDestinations, ref nextPosition); + BitmapHelper.FlagMultipleBits(pushDataLength, invalidJumpDestinations, ref nextPosition); } position = nextPosition; @@ -1229,7 +1221,7 @@ private static bool ValidateReturnContract( } else { - containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateInitcodeMode; + containersWorklist.VisitedContainers[0] = ValidationStrategy.ValidateInitCodeMode; } } @@ -1259,7 +1251,7 @@ private static bool ValidateReturnContract( } containersWorklist.Enqueue(runtimeContainerId + 1, ValidationStrategy.ValidateRuntimeMode | ValidationStrategy.ValidateFullBody); - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref pos); + BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref pos); return true; } @@ -1288,17 +1280,17 @@ private static bool ValidateRelativeJump( } short offset = code.Slice(pos, TWO_BYTE_LENGTH).ReadEthInt16(); - int rjumpDest = offset + TWO_BYTE_LENGTH + pos; + int rJumpDest = offset + TWO_BYTE_LENGTH + pos; - if (rjumpDest < 0 || rjumpDest >= code.Length) + if (rJumpDest < 0 || rJumpDest >= code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, {opcode.FastToString()} Destination outside of Code bounds"); return false; } - BitmapHelper.HandleNumbits(ONE_BYTE_LENGTH, jumpDestinations, ref rjumpDest); - BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); + BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, jumpDestinations, ref rJumpDest); + BitmapHelper.FlagMultipleBits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); return true; } @@ -1345,17 +1337,17 @@ private static bool ValidateRelativeJumpV( 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) + 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.HandleNumbits(ONE_BYTE_LENGTH, jumpDestinations, ref rjumpDest); + BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, jumpDestinations, ref rJumpDest); } - BitmapHelper.HandleNumbits(immediateValueSize, invalidJumpDestinations, ref pos); + BitmapHelper.FlagMultipleBits(immediateValueSize, invalidJumpDestinations, ref pos); return true; } @@ -1405,7 +1397,7 @@ private static bool ValidateCallF( } sectionsWorklist.Enqueue(targetSectionId, strategy); - BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); + BitmapHelper.FlagMultipleBits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); return true; } @@ -1439,7 +1431,7 @@ private static bool ValidateDataLoadN( return false; } - BitmapHelper.HandleNumbits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); + BitmapHelper.FlagMultipleBits(TWO_BYTE_LENGTH, invalidJumpDestinations, ref pos); return true; } @@ -1477,15 +1469,15 @@ private static bool ValidateEofCreate( } if (containersWorklist.VisitedContainers[initCodeSectionId + 1] != 0 && - containersWorklist.VisitedContainers[initCodeSectionId + 1] != ValidationStrategy.ValidateInitcodeMode) + 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.HandleNumbits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref pos); + containersWorklist.Enqueue(initCodeSectionId + 1, ValidationStrategy.ValidateInitCodeMode | ValidationStrategy.ValidateFullBody); + BitmapHelper.FlagMultipleBits(ONE_BYTE_LENGTH, invalidJumpDestinations, ref pos); return true; } @@ -1501,19 +1493,19 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re ushort currentSectionOutputs = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (ushort)0 : typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; short peakStackHeight = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - var unreachedBytes = code.Length; - var isTargetSectionNonReturning = false; - var programCounter = 0; + int unreachedBytes = code.Length; + bool isTargetSectionNonReturning = false; + int programCounter = 0; recordedStackHeight[0].Max = peakStackHeight; recordedStackHeight[0].Min = peakStackHeight; StackBounds currentStackBounds = recordedStackHeight[0]; while (programCounter < code.Length) { - var opcode = (Instruction)code[programCounter]; - (var inputs, var outputs, var immediates) = opcode.StackRequirements(); + Instruction opcode = (Instruction)code[programCounter]; + (ushort? inputCount, ushort? outputCount, ushort? immediateCount) = opcode.StackRequirements(); - var posPostInstruction = (ushort)(programCounter + 1); + int posPostInstruction = programCounter + 1; if (posPostInstruction > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); @@ -1523,51 +1515,51 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re switch (opcode) { case Instruction.CALLF or Instruction.JUMPF: - ushort targetSectionId = code.Slice(posPostInstruction, immediates.Value).ReadEthUInt16(); - inputs = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; + ushort targetSectionId = code.Slice(posPostInstruction, immediateCount.Value).ReadEthUInt16(); + inputCount = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - outputs = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + outputCount = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; isTargetSectionNonReturning = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; - outputs = (ushort)(isTargetSectionNonReturning ? 0 : outputs); + outputCount = (ushort)(isTargetSectionNonReturning ? 0 : outputCount); int targetMaxStackHeight = typeSection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); - if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputs < currentStackBounds.Max) + if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputCount < currentStackBounds.Max) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callf must not exceed {MAX_STACK_HEIGHT}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callF must not exceed {MAX_STACK_HEIGHT}"); return false; } - if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currentSectionOutputs + inputs - outputs == currentStackBounds.Min && currentStackBounds.BoundsEqual())) + if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currentSectionOutputs + inputCount - outputCount == currentStackBounds.Min && currentStackBounds.BoundsEqual())) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currentSectionOutputs + inputs - outputs} but found {currentStackBounds.Max}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currentSectionOutputs + inputCount - outputCount} but found {currentStackBounds.Max}"); return false; } break; case Instruction.DUPN: - var imm_n = 1 + code[posPostInstruction]; - inputs = (ushort)imm_n; - outputs = (ushort)(inputs + 1); + int imm_n = 1 + code[posPostInstruction]; + inputCount = (ushort)imm_n; + outputCount = (ushort)(inputCount + 1); break; case Instruction.SWAPN: imm_n = 1 + code[posPostInstruction]; - outputs = inputs = (ushort)(1 + imm_n); + outputCount = inputCount = (ushort)(1 + imm_n); break; case Instruction.EXCHANGE: imm_n = 1 + (byte)(code[posPostInstruction] >> 4); - var imm_m = 1 + (byte)(code[posPostInstruction] & 0x0F); - outputs = inputs = (ushort)(imm_n + imm_m + 1); + int imm_m = 1 + (byte)(code[posPostInstruction] & 0x0F); + outputCount = inputCount = (ushort)(imm_n + imm_m + 1); break; } - if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputs) + if ((isTargetSectionNonReturning || opcode is not Instruction.JUMPF) && currentStackBounds.Min < inputCount) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputs} but found {currentStackBounds.Min}"); + if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack Underflow required {inputCount} but found {currentStackBounds.Min}"); return false; } if (!opcode.IsTerminating()) { - var delta = (short)(outputs - inputs); + short delta = (short)(outputCount - inputCount); currentStackBounds.Max += delta; currentStackBounds.Min += delta; } @@ -1577,7 +1569,7 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re { case Instruction.RETF: { - var expectedHeight = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; + 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}"); @@ -1587,11 +1579,11 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re } case Instruction.RJUMP or Instruction.RJUMPI: { - short offset = code.Slice(programCounter + 1, immediates.Value).ReadEthInt16(); - var jumpDestination = posPostInstruction + immediates.Value + offset; + short offset = code.Slice(programCounter + 1, immediateCount.Value).ReadEthInt16(); + int jumpDestination = posPostInstruction + immediateCount.Value + offset; - if (opcode is Instruction.RJUMPI && (posPostInstruction + immediates.Value < recordedStackHeight.Length)) - recordedStackHeight[posPostInstruction + immediates.Value].Combine(currentStackBounds); + if (opcode is Instruction.RJUMPI && (posPostInstruction + immediateCount.Value < recordedStackHeight.Length)) + recordedStackHeight[posPostInstruction + immediateCount.Value].Combine(currentStackBounds); if (jumpDestination > programCounter) recordedStackHeight[jumpDestination].Combine(currentStackBounds); @@ -1608,15 +1600,17 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re } case Instruction.RJUMPV: { - var count = code[posPostInstruction] + 1; - immediates = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); + int count = code[posPostInstruction] + 1; + immediateCount = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); for (short j = 0; j < count; j++) { int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); - var jumpDestination = posPostInstruction + immediates.Value + offset; + int jumpDestination = posPostInstruction + immediateCount.Value + offset; if (jumpDestination > programCounter) + { recordedStackHeight[jumpDestination].Combine(currentStackBounds); + } else { if (recordedStackHeight[jumpDestination] != currentStackBounds) @@ -1627,7 +1621,7 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re } } - posPostInstruction += immediates.Value; + posPostInstruction += immediateCount.Value; if (posPostInstruction > code.Length) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); @@ -1637,8 +1631,8 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re } } - unreachedBytes -= 1 + immediates.Value; - programCounter += 1 + immediates.Value; + unreachedBytes -= 1 + immediateCount.Value; + programCounter += 1 + immediateCount.Value; if (opcode.IsTerminating()) { @@ -1674,7 +1668,7 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re return false; } - var result = peakStackHeight < MAX_STACK_HEIGHT; + bool result = peakStackHeight < MAX_STACK_HEIGHT; if (!result) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); @@ -1693,15 +1687,9 @@ 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 Enqueue(int index, ValidationStrategy strategy) => ContainerQueue.Enqueue((index, strategy)); - public void MarkVisited(int index, ValidationStrategy strategy) - { - VisitedContainers[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); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index d4b0bc575e6..246a0d05ea4 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -96,7 +96,9 @@ public static EvmExceptionType InstructionReturnDataCopy(V 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). gasAvailable -= GasCostOf.VeryLow + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size, out bool outOfGas); @@ -162,8 +164,8 @@ public static EvmExceptionType InstructionDataLoad(VirtualMachine vm, ref EvmSta // 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 zpbytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); - stack.PushBytes(zpbytes); + ZeroPaddedSpan bytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); + stack.PushBytes(bytes); return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor. @@ -190,8 +192,8 @@ public static EvmExceptionType InstructionDataLoadN(VirtualMachine vm, ref EvmSt // 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 zpbytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); - stack.PushBytes(zpbytes); + ZeroPaddedSpan bytes = codeInfo.DataSection.SliceWithZeroPadding(offset, 32); + stack.PushBytes(bytes); // Advance the program counter past the immediate operand. programCounter += EofValidator.TWO_BYTE_LENGTH; @@ -243,12 +245,16 @@ public static EvmExceptionType InstructionDataCopy(Virtual 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 (!UpdateGas(GasCostOf.DataCopy + GasCostOf.Memory * EvmPooledMemory.Div32Ceiling(in size, out bool outOfGas), ref gasAvailable) || outOfGas) + { goto OutOfGas; + } if (!size.IsZero) { @@ -356,7 +362,7 @@ public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmSt // 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 immediates = (ushort)(count * EofValidator.TWO_BYTE_LENGTH + EofValidator.ONE_BYTE_LENGTH); + 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. @@ -364,8 +370,8 @@ public static EvmExceptionType InstructionJumpTable(VirtualMachine vm, ref EvmSt int offset = codeSection.Slice(case_v, EofValidator.TWO_BYTE_LENGTH).ReadEthInt16(); programCounter += offset; } - // Skip over the jump table immediates. - programCounter += immediates; + // Skip over the jump table immediateCount. + programCounter += immediateCount; return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor. @@ -600,38 +606,39 @@ public static EvmExceptionType InstructionEofCreate(Virtua IReleaseSpec spec = vm.Spec; ref readonly ExecutionEnvironment env = ref vm.EvmState.Env; - ICodeInfo codeInfo = env.CodeInfo; - if (codeInfo.Version == 0) + if (env.CodeInfo.Version == 0) goto BadInstruction; if (vm.EvmState.IsStatic) goto StaticCallViolation; // Cast the current code info to EOF-specific container type. - EofCodeInfo container = codeInfo as EofCodeInfo; + EofCodeInfo container = env.CodeInfo as EofCodeInfo; ExecutionType currentContext = ExecutionType.EOFCREATE; // 1. Deduct the creation gas cost. if (!UpdateGas(GasCostOf.TxCreate, ref gasAvailable)) goto OutOfGas; - ReadOnlySpan codeSection = codeInfo.CodeSection.Span; + ReadOnlySpan codeSection = container.CodeSection.Span; // 2. Read the immediate operand for the init container index. - int initcontainerIndex = codeSection[programCounter++]; + 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 (!UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataSize)) goto OutOfGas; - // 5. Load the init code (EOF subcontainer) from the container using the given index. - ReadOnlySpan initContainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initcontainerIndex).Value]; + // 5. Load the init code (EOF subContainer) from the container using the given index. + ReadOnlySpan initContainer = container.ContainerSection.Span[(Range)container.ContainerSectionOffset(initContainerIndex).Value]; // EIP-3860: Check that the init code size does not exceed the maximum allowed. if (spec.IsEip3860Enabled) { @@ -656,8 +663,8 @@ public static EvmExceptionType InstructionEofCreate(Virtua return EvmExceptionType.None; } - // 8. Prepare the calldata from the caller’s memory slice. - Span calldata = vm.EvmState.Memory.LoadSpan(dataOffset, dataSize); + // 8. Prepare the callData from the caller’s memory slice. + Span callData = vm.EvmState.Memory.LoadSpan(dataOffset, dataSize); // 9. Determine gas available for the new contract execution, applying the 63/64 rule if enabled. long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; @@ -709,7 +716,7 @@ public static EvmExceptionType InstructionEofCreate(Virtua state.SubtractFromBalance(env.ExecutingAccount, value, spec); // Create new code info for the init code. - ICodeInfo codeinfo = CodeInfoFactory.CreateCodeInfo(initContainer.ToArray(), spec, ValidationStrategy.ExractHeader); + ICodeInfo codeInfo = CodeInfoFactory.CreateCodeInfo(initContainer.ToArray(), spec, ValidationStrategy.ExractHeader); // Set up the execution environment for the new contract. ExecutionEnvironment callEnv = new @@ -719,8 +726,8 @@ public static EvmExceptionType InstructionEofCreate(Virtua caller: env.ExecutingAccount, executingAccount: contractAddress, codeSource: null, - codeInfo: codeinfo, - inputData: calldata.ToArray(), + codeInfo: codeInfo, + inputData: callData.ToArray(), transferValue: value, value: value ); @@ -826,7 +833,7 @@ public static EvmExceptionType InstructionReturnDataLoad(VirtualMachine vm, ref /// /// 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, + /// 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. /// /// @@ -857,7 +864,9 @@ public static EvmExceptionType InstructionEofCall targetBytes) || !stack.PopUInt256(out UInt256 dataOffset) || !stack.PopUInt256(out UInt256 dataLength)) + { goto StackUnderflow; + } UInt256 transferValue; UInt256 callValue; From df45003fb9ab25d1676e7c9155b2aaf46a28f5f4 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 6 Feb 2025 05:31:42 +0000 Subject: [PATCH 254/255] Refactor --- .../EvmObjectFormat/Handlers/EofV1.cs | 421 +++++++++++++----- 1 file changed, 307 insertions(+), 114 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index d8ac4d1dd72..109a8d8f0bd 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -1481,21 +1481,42 @@ private static bool ValidateEofCreate( 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()); try { - ushort suggestedMaxHeight = typeSection.Slice(sectionId * MINIMUM_TYPESECTION_SIZE + TWO_BYTE_LENGTH, TWO_BYTE_LENGTH).ReadEthUInt16(); + // 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 0x80 indicates non-returning. + ushort currentSectionOutputs = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 + ? (ushort)0 + : typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - ushort currentSectionOutputs = typeSection[sectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80 ? (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; - bool isTargetSectionNonReturning = false; int programCounter = 0; + // Initialize the recorded stack bounds for the starting instruction. recordedStackHeight[0].Max = peakStackHeight; recordedStackHeight[0].Min = peakStackHeight; StackBounds currentStackBounds = recordedStackHeight[0]; @@ -1503,60 +1524,48 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re while (programCounter < code.Length) { Instruction opcode = (Instruction)code[programCounter]; - (ushort? inputCount, ushort? outputCount, ushort? immediateCount) = opcode.StackRequirements(); + (ushort? baseInput, ushort? baseOutput, ushort? baseImmediate) = opcode.StackRequirements(); int posPostInstruction = programCounter + 1; if (posPostInstruction > code.Length) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); return false; } - switch (opcode) - { - case Instruction.CALLF or Instruction.JUMPF: - ushort targetSectionId = code.Slice(posPostInstruction, immediateCount.Value).ReadEthUInt16(); - inputCount = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + INPUTS_OFFSET]; - - outputCount = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET]; - isTargetSectionNonReturning = typeSection[targetSectionId * MINIMUM_TYPESECTION_SIZE + OUTPUTS_OFFSET] == 0x80; - outputCount = (ushort)(isTargetSectionNonReturning ? 0 : outputCount); - int targetMaxStackHeight = typeSection.Slice(targetSectionId * MINIMUM_TYPESECTION_SIZE + MAX_STACK_HEIGHT_OFFSET, TWO_BYTE_LENGTH).ReadEthUInt16(); - - if (MAX_STACK_HEIGHT - targetMaxStackHeight + inputCount < currentStackBounds.Max) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, stack head during callF must not exceed {MAX_STACK_HEIGHT}"); - return false; - } + ushort inputCount = baseInput ?? 0; + ushort outputCount = baseOutput ?? 0; + ushort immediateCount = baseImmediate ?? 0; + bool isTargetSectionNonReturning = false; - if (opcode is Instruction.JUMPF && !isTargetSectionNonReturning && !(currentSectionOutputs + inputCount - outputCount == currentStackBounds.Min && currentStackBounds.BoundsEqual())) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack State invalid, required height {currentSectionOutputs + inputCount - outputCount} but found {currentStackBounds.Max}"); - return false; - } - break; - case Instruction.DUPN: - int imm_n = 1 + code[posPostInstruction]; - inputCount = (ushort)imm_n; - outputCount = (ushort)(inputCount + 1); - break; - case Instruction.SWAPN: - imm_n = 1 + code[posPostInstruction]; - outputCount = inputCount = (ushort)(1 + imm_n); - break; - case Instruction.EXCHANGE: - imm_n = 1 + (byte)(code[posPostInstruction] >> 4); - int imm_m = 1 + (byte)(code[posPostInstruction] & 0x0F); - outputCount = inputCount = (ushort)(imm_n + imm_m + 1); - break; + // 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 + { + var 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}"); + 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); @@ -1565,82 +1574,35 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re } peakStackHeight = Math.Max(peakStackHeight, currentStackBounds.Max); - switch (opcode) + // Process control-flow opcodes. + if (opcode == Instruction.RETF) { - case Instruction.RETF: - { - 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; - } - break; - } - case Instruction.RJUMP or Instruction.RJUMPI: - { - short offset = code.Slice(programCounter + 1, immediateCount.Value).ReadEthInt16(); - int jumpDestination = posPostInstruction + immediateCount.Value + offset; - - if (opcode is Instruction.RJUMPI && (posPostInstruction + immediateCount.Value < recordedStackHeight.Length)) - recordedStackHeight[posPostInstruction + immediateCount.Value].Combine(currentStackBounds); - - if (jumpDestination > programCounter) - recordedStackHeight[jumpDestination].Combine(currentStackBounds); - else - { - if (recordedStackHeight[jumpDestination] != currentStackBounds) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); - return false; - } - } - - break; - } - case Instruction.RJUMPV: - { - int count = code[posPostInstruction] + 1; - immediateCount = (ushort)(count * TWO_BYTE_LENGTH + ONE_BYTE_LENGTH); - for (short j = 0; j < count; j++) - { - int case_v = posPostInstruction + ONE_BYTE_LENGTH + j * TWO_BYTE_LENGTH; - int offset = code.Slice(case_v, TWO_BYTE_LENGTH).ReadEthInt16(); - int jumpDestination = posPostInstruction + immediateCount.Value + offset; - if (jumpDestination > programCounter) - { - recordedStackHeight[jumpDestination].Combine(currentStackBounds); - } - else - { - if (recordedStackHeight[jumpDestination] != currentStackBounds) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); - return false; - } - } - } - - posPostInstruction += immediateCount.Value; - if (posPostInstruction > code.Length) - { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, PC Reached out of bounds"); - return false; - } - break; - } + if (!ValidateReturnInstruction(sectionId, typeSection, currentStackBounds)) + return false; + } + else if (opcode is Instruction.RJUMP or Instruction.RJUMPI) + { + if (!ValidateRelativeJumpInstruction(opcode, programCounter, posPostInstruction, immediateCount, code, currentStackBounds, recordedStackHeight)) + return false; + } + else if (opcode == Instruction.RJUMPV) + { + if (!ValidateRelativeJumpVInstruction(programCounter, posPostInstruction, currentStackBounds, recordedStackHeight, code, out immediateCount, out posPostInstruction)) + return false; } - unreachedBytes -= 1 + immediateCount.Value; - programCounter += 1 + immediateCount.Value; + unreachedBytes -= 1 + immediateCount; + programCounter += 1 + immediateCount; + // Propagate recorded stack bounds for subsequent instructions. if (opcode.IsTerminating()) { if (programCounter < code.Length) { if (recordedStackHeight[programCounter].Max < 0) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, opcode not forward referenced, section {sectionId} pc {programCounter}"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, opcode not forward referenced, section {sectionId} pc {programCounter}"); return false; } currentStackBounds = recordedStackHeight[programCounter]; @@ -1658,23 +1620,26 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re if (unreachedBytes != 0) { - if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, bytecode has unreachable segments"); + 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}"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Suggested Max Stack height mismatches with actual Max, expected {suggestedMaxHeight} but found {peakStackHeight}"); return false; } - bool result = peakStackHeight < MAX_STACK_HEIGHT; - if (!result) + 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}"); + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, stack overflow exceeded max stack height of {MAX_STACK_HEIGHT} but found {peakStackHeight}"); return false; } - return result; + + return true; } finally { @@ -1682,6 +1647,234 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re } } + /// + /// 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, + 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 == 0x80; + 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 type section metadata. + /// The current stack bounds. + /// True if the RETF instruction’s requirements are met; otherwise, false. + private static bool ValidateReturnInstruction(int sectionId, ReadOnlySpan typeSection, StackBounds currentStackBounds) + { + 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 full bytecode. + /// The current stack bounds. + /// The array of recorded stack bounds per code offset. + /// True if the jump destination’s stack state is valid; otherwise, false. + private static bool ValidateRelativeJumpInstruction( + Instruction opcode, + int programCounter, + int posPostInstruction, + ushort immediateCount, + ReadOnlySpan code, + StackBounds currentStackBounds, + StackBounds[] recordedStackHeight) + { + // 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, + 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, + StackBounds currentStackBounds, + StackBounds[] recordedStackHeight) + { + if (jumpDestination > programCounter) + { + recordedStackHeight[jumpDestination].Combine(currentStackBounds); + } + else if (recordedStackHeight[jumpDestination] != currentStackBounds) + { + if (Logger.IsTrace) + Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); + return false; + } + return true; + } + private readonly struct QueueManager(int containerCount) { public readonly Queue<(int index, ValidationStrategy strategy)> ContainerQueue = new(); From 97b4d0851efaf089ccb6e666a6ad34cc49e20516 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 6 Feb 2025 10:12:28 +0000 Subject: [PATCH 255/255] readonly struct --- .../EvmObjectFormat/Handlers/EofV1.cs | 90 ++++++++++--------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs index 109a8d8f0bd..4986a50f029 100644 --- a/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs +++ b/src/Nethermind/Nethermind.Evm/EvmObjectFormat/Handlers/EofV1.cs @@ -1497,7 +1497,7 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re { // Rent an array to record the stack bounds at each code offset. StackBounds[] recordedStackHeight = ArrayPool.Shared.Rent(code.Length); - Array.Fill(recordedStackHeight, new StackBounds()); + Array.Fill(recordedStackHeight, new StackBounds(min: 1023, max: -1)); try { @@ -1517,8 +1517,7 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re int unreachedBytes = code.Length; int programCounter = 0; // Initialize the recorded stack bounds for the starting instruction. - recordedStackHeight[0].Max = peakStackHeight; - recordedStackHeight[0].Min = peakStackHeight; + recordedStackHeight[0] = new(peakStackHeight, peakStackHeight); StackBounds currentStackBounds = recordedStackHeight[0]; while (programCounter < code.Length) @@ -1544,7 +1543,7 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re { try { - var mod = ApplyOpcodeImmediateModifiers(opcode, posPostInstruction, currentSectionOutputs, immediateCount, currentStackBounds, code, typeSection); + InstructionModificationResult mod = ApplyOpcodeImmediateModifiers(opcode, posPostInstruction, currentSectionOutputs, immediateCount, currentStackBounds, code, typeSection); inputCount = mod.InputCount; outputCount = mod.OutputCount; immediateCount = mod.ImmediateCount; @@ -1569,20 +1568,19 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re if (!opcode.IsTerminating()) { short delta = (short)(outputCount - inputCount); - currentStackBounds.Max += delta; - currentStackBounds.Min += delta; + 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, typeSection, currentStackBounds)) + if (!ValidateReturnInstruction(sectionId, currentStackBounds, typeSection)) return false; } else if (opcode is Instruction.RJUMP or Instruction.RJUMPI) { - if (!ValidateRelativeJumpInstruction(opcode, programCounter, posPostInstruction, immediateCount, code, currentStackBounds, recordedStackHeight)) + if (!ValidateRelativeJumpInstruction(opcode, programCounter, posPostInstruction, immediateCount, currentStackBounds, recordedStackHeight, code)) return false; } else if (opcode == Instruction.RJUMPV) @@ -1599,21 +1597,23 @@ public static bool ValidateStackState(int sectionId, ReadOnlySpan code, Re { if (programCounter < code.Length) { - if (recordedStackHeight[programCounter].Max < 0) + 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 = recordedStackHeight[programCounter]; + currentStackBounds = recordedBounds; } } else { if (programCounter < code.Length) { - recordedStackHeight[programCounter].Combine(currentStackBounds); - currentStackBounds = recordedStackHeight[programCounter]; + ref StackBounds recordedBounds = ref recordedStackHeight[programCounter]; + recordedBounds.Combine(currentStackBounds); + currentStackBounds = recordedBounds; } } } @@ -1678,7 +1678,7 @@ private static InstructionModificationResult ApplyOpcodeImmediateModifiers( int posPostInstruction, ushort currentSectionOutputs, ushort immediateCount, - StackBounds currentStackBounds, + in StackBounds currentStackBounds, ReadOnlySpan code, ReadOnlySpan typeSection) { @@ -1751,10 +1751,10 @@ private static InstructionModificationResult ApplyOpcodeImmediateModifiers( /// the expected output count for the section. /// /// The identifier of the current section. - /// The type section metadata. /// 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, ReadOnlySpan typeSection, StackBounds currentStackBounds) + 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()) @@ -1774,18 +1774,18 @@ private static bool ValidateReturnInstruction(int sectionId, ReadOnlySpan /// The current program counter. /// The offset immediately after the opcode. /// The immediate count associated with the opcode. - /// The full bytecode. /// 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, - ReadOnlySpan code, - StackBounds currentStackBounds, - StackBounds[] recordedStackHeight) + in StackBounds currentStackBounds, + StackBounds[] recordedStackHeight, + ReadOnlySpan code) { // Read the jump offset from the immediate bytes. short offset = code.Slice(programCounter + 1, immediateCount).ReadEthInt16(); @@ -1813,7 +1813,7 @@ private static bool ValidateRelativeJumpInstruction( private static bool ValidateRelativeJumpVInstruction( int programCounter, int posPostInstruction, - StackBounds currentStackBounds, + in StackBounds currentStackBounds, StackBounds[] recordedStackHeight, ReadOnlySpan code, out ushort updatedImmediateCount, @@ -1859,14 +1859,15 @@ private static bool ValidateRelativeJumpVInstruction( private static bool ValidateJumpDestination( int jumpDestination, int programCounter, - StackBounds currentStackBounds, + in StackBounds currentStackBounds, StackBounds[] recordedStackHeight) { + ref StackBounds recordedBounds = ref recordedStackHeight[jumpDestination]; if (jumpDestination > programCounter) { - recordedStackHeight[jumpDestination].Combine(currentStackBounds); + recordedBounds.Combine(currentStackBounds); } - else if (recordedStackHeight[jumpDestination] != currentStackBounds) + else if (recordedBounds != currentStackBounds) { if (Logger.IsTrace) Logger.Trace($"EOF: Eof{VERSION}, Stack state invalid at {jumpDestination}"); @@ -1875,6 +1876,7 @@ private static bool ValidateJumpDestination( return true; } + [StructLayout(LayoutKind.Auto)] private readonly struct QueueManager(int containerCount) { public readonly Queue<(int index, ValidationStrategy strategy)> ContainerQueue = new(); @@ -1889,26 +1891,7 @@ private readonly struct QueueManager(int containerCount) public bool IsAllVisited() => VisitedContainers.All(x => x != 0); } - [StructLayout(LayoutKind.Sequential)] - private struct StackBounds() - { - public short Max = -1; - public short Min = 1023; - - public void Combine(StackBounds other) - { - Max = Math.Max(Max, other.Max); - Min = Math.Min(Min, other.Min); - } - - public readonly bool BoundsEqual() => Max == Min; - - public static bool operator ==(StackBounds left, StackBounds right) => left.Max == right.Max && right.Min == left.Min; - public static bool operator !=(StackBounds left, 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; - } - + [StructLayout(LayoutKind.Auto)] private ref struct Sizes { public ushort? TypeSectionSize; @@ -1927,3 +1910,24 @@ internal enum Separator : byte } } +[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)); + } +}