diff --git a/src/Nethermind/Nethermind.Evm/GasPolicy/EthereumGasPolicy.cs b/src/Nethermind/Nethermind.Evm/GasPolicy/EthereumGasPolicy.cs index 3bc803f9e729..729e869c3905 100644 --- a/src/Nethermind/Nethermind.Evm/GasPolicy/EthereumGasPolicy.cs +++ b/src/Nethermind/Nethermind.Evm/GasPolicy/EthereumGasPolicy.cs @@ -65,6 +65,10 @@ public static bool ConsumeStateGas(ref EthereumGasPolicy gas, long stateGasCost) return true; } + public static bool TryConsumeStateAndRegularGas(ref EthereumGasPolicy gas, long stateGasCost, long regularGasCost) => + (regularGasCost <= 0 || UpdateGas(ref gas, regularGasCost)) && + (stateGasCost <= 0 || ConsumeStateGas(ref gas, stateGasCost)); + public static bool ConsumeSelfDestructGas(ref EthereumGasPolicy gas) => UpdateGas(ref gas, GasCostOf.SelfDestructEip150); @@ -196,6 +200,21 @@ public static bool UpdateGas(ref EthereumGasPolicy gas, return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ConsumeStorageWrite(ref EthereumGasPolicy gas, IReleaseSpec spec) + where TEip8037 : struct, IFlag + where TIsSlotCreation : struct, IFlag + { + if (!TIsSlotCreation.IsActive) return UpdateGas(ref gas, spec.GasCosts.SStoreResetCost); + return TEip8037.IsActive switch + { + // EIP-8037: charge the regular component first so an OOG halt does not + // spill state gas into gas_left and then restore it to the parent frame. + true => TryConsumeStateAndRegularGas(ref gas, GasCostOf.SSetState, GasCostOf.SSetRegular), + false => UpdateGas(ref gas, GasCostOf.SSet), + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UpdateGasUp(ref EthereumGasPolicy gas, long refund) @@ -230,8 +249,14 @@ public static bool ConsumeCallValueTransfer(ref EthereumGasPolicy gas) => UpdateGas(ref gas, GasCostOf.CallValue); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ConsumeNewAccountCreation(ref EthereumGasPolicy gas) - => ConsumeStateGas(ref gas, GasCostOf.NewAccountState); + public static bool ConsumeNewAccountCreation(ref EthereumGasPolicy gas) where TEip8037 : struct, IFlag + { + return TEip8037.IsActive switch + { + true => ConsumeStateGas(ref gas, GasCostOf.NewAccountState), + false => UpdateGas(ref gas, GasCostOf.NewAccount) + }; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool ConsumeLogEmission(ref EthereumGasPolicy gas, long topicCount, long dataSize) diff --git a/src/Nethermind/Nethermind.Evm/GasPolicy/IGasPolicy.cs b/src/Nethermind/Nethermind.Evm/GasPolicy/IGasPolicy.cs index 64daecbc1a64..a2fcfb32598e 100644 --- a/src/Nethermind/Nethermind.Evm/GasPolicy/IGasPolicy.cs +++ b/src/Nethermind/Nethermind.Evm/GasPolicy/IGasPolicy.cs @@ -215,9 +215,7 @@ static virtual bool ConsumeStateGas(ref TSelf gas, long stateGasCost) => /// State gas component. /// Regular gas component. /// true if both deductions succeeded; otherwise, false. - static virtual bool TryConsumeStateAndRegularGas(ref TSelf gas, long stateGasCost, long regularGasCost) => - (regularGasCost <= 0 || TSelf.UpdateGas(ref gas, regularGasCost)) && - (stateGasCost <= 0 || TSelf.ConsumeStateGas(ref gas, stateGasCost)); + static abstract bool TryConsumeStateAndRegularGas(ref TSelf gas, long stateGasCost, long regularGasCost); /// /// Refunds gas by adding the specified amount back to the available gas. @@ -226,6 +224,18 @@ static virtual bool TryConsumeStateAndRegularGas(ref TSelf gas, long stateGasCos /// The gas amount to refund. static abstract void UpdateGasUp(ref TSelf gas, long refund); + /// + /// Charges gas for SSTORE write operation (after cold/warm access cost). + /// Cost is calculated internally based on whether it's a slot creation or update. + /// + /// The gas state to update. + /// True if creating a new slot (original was zero). + /// The release specification for determining reset cost. + /// True if sufficient gas available + static abstract bool ConsumeStorageWrite(ref TSelf gas, IReleaseSpec spec) + where TEip8037 : struct, IFlag + where TIsSlotCreation : struct, IFlag; + /// /// Refunds state gas back to the state reservoir. /// Pre-EIP-8037 fallback refunds into regular gas. @@ -262,7 +272,7 @@ static virtual long ApplyCodeInsertRefunds(ref TSelf gas, int codeInsertRefunds, /// /// The gas state to update. /// True if sufficient gas available - static abstract bool ConsumeNewAccountCreation(ref TSelf gas); + static abstract bool ConsumeNewAccountCreation(ref TSelf gas) where TEip8037 : struct, IFlag; /// /// Charges gas for LOG emission with topic and data costs. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index 974f7984139c..7d48f3a42df0 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -185,11 +185,7 @@ public static EvmExceptionType InstructionCall transferValue != 0 && state.IsDeadAccount(target), }; - bool newAccountOutOfGas = chargesNewAccount && !(TEip8037.IsActive switch - { - true => TGasPolicy.ConsumeNewAccountCreation(ref gas), - false => TGasPolicy.UpdateGas(ref gas, GasCostOf.NewAccount), - }); + bool newAccountOutOfGas = chargesNewAccount && !TGasPolicy.ConsumeNewAccountCreation(ref gas); if (newAccountOutOfGas) goto OutOfGas; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs index 1c27b19a9b55..2bb3bb6350b1 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs @@ -246,11 +246,7 @@ private static EvmExceptionType InstructionSelfDestruct !inheritorAccountExists && spec.UseShanghaiDDosProtection, }; - bool outOfGas = chargesNewAccount && !(TEip8037.IsActive switch - { - true => TGasPolicy.ConsumeNewAccountCreation(ref gas), - false => TGasPolicy.UpdateGas(ref gas, GasCostOf.NewAccount), - }); + bool outOfGas = chargesNewAccount && !(TGasPolicy.ConsumeNewAccountCreation(ref gas)); if (outOfGas) goto OutOfGas; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index b6730d09a725..bfc66f0abaf5 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -505,19 +505,12 @@ internal static EvmExceptionType InstructionSStoreMetered !TGasPolicy.TryConsumeStateAndRegularGas(ref gas, GasCostOf.SSetState, GasCostOf.SSetRegular), - false => !TGasPolicy.UpdateGas(ref gas, GasCostOf.SSet), - }; - + bool ssetOutOfGas = !TGasPolicy.ConsumeStorageWrite(ref gas, spec); if (ssetOutOfGas) goto OutOfGas; } else { - if (!TGasPolicy.UpdateGas(ref gas, spec.GasCosts.SStoreResetCost)) + if (!TGasPolicy.ConsumeStorageWrite(ref gas, spec)) goto OutOfGas; if (newIsZero)