From ed3f6f5a7079257e98528c5f75179a79833d1d53 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 16 Jan 2024 17:25:43 +0100 Subject: [PATCH 001/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] - 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] - 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] * 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/159] 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/159] 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/159] 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/159] 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/159] * (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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] - 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] * 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] * 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/159] * 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] * 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/159] 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/159] * 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/159] * 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/159] * 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/159] * 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/159] * 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/159] * 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/159] 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/159] 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/159] 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/159] * 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/159] * 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/159] 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/159] * 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/159] 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/159] * 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/159] * 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] * 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/159] * 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/159] * 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/159] * 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/159] * 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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/159] 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;