Skip to content

Commit cb8645c

Browse files
refactor(evm): remove chargeForWarm parameter from ConsumeAccountAccessGas
AccountAccessKind enum already encodes whether warm charge should be applied — SelfDestructBeneficiary skips warm charge, Default always charges. The chargeForWarm boolean was only ever false in the SELFDESTRUCT context, making it fully redundant with the kind discriminator. Constraint: chargeForWarm was only false at the SELFDESTRUCT call site Rejected: Keep both parameters for flexibility | no other caller needs chargeForWarm=false
1 parent 93cc6e0 commit cb8645c

4 files changed

Lines changed: 51 additions & 59 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
namespace Nethermind.Evm.GasPolicy;
5+
6+
/// <summary>
7+
/// Discriminator for account access gas charging.
8+
/// Allows <see cref="IGasPolicy{TSelf}.ConsumeAccountAccessGas"/> to vary
9+
/// its resource-kind split based on the calling opcode's semantics.
10+
/// </summary>
11+
public enum AccountAccessKind : byte
12+
{
13+
/// <summary>
14+
/// Regular account access (BALANCE, EXTCODESIZE, CALL, etc.).
15+
/// </summary>
16+
Default = 0,
17+
18+
/// <summary>
19+
/// SELFDESTRUCT beneficiary access.
20+
/// Cold access charges full cost to StorageAccess (no Computation split);
21+
/// warm access charges nothing.
22+
/// </summary>
23+
SelfDestructBeneficiary = 1
24+
}

src/Nethermind/Nethermind.Evm/GasPolicy/EthereumGasPolicy.cs

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
22
// SPDX-License-Identifier: LGPL-3.0-only
33

4+
using System;
45
using System.Diagnostics;
56
using System.Diagnostics.CodeAnalysis;
67
using System.IO;
@@ -51,61 +52,46 @@ public static bool ConsumeAccountAccessGasWithDelegation(ref EthereumGasPolicy g
5152
ref readonly StackAccessTracker accessTracker,
5253
bool isTracingAccess,
5354
Address address,
54-
Address? delegated,
55-
bool chargeForWarm = true)
55+
Address? delegated)
5656
{
5757
if (!spec.UseHotAndColdStorage)
5858
return true;
5959

60-
bool notOutOfGas = ConsumeAccountAccessGas(ref gas, spec, in accessTracker, isTracingAccess, address, chargeForWarm);
61-
return notOutOfGas && (delegated is null || ConsumeAccountAccessGas(ref gas, spec, in accessTracker, isTracingAccess, delegated, chargeForWarm));
60+
bool notOutOfGas = ConsumeAccountAccessGas(ref gas, spec, in accessTracker, isTracingAccess, address);
61+
return notOutOfGas && (delegated is null || ConsumeAccountAccessGas(ref gas, spec, in accessTracker, isTracingAccess, delegated));
6262
}
6363

6464
public static bool ConsumeAccountAccessGas(ref EthereumGasPolicy gas,
6565
IReleaseSpec spec,
6666
ref readonly StackAccessTracker accessTracker,
6767
bool isTracingAccess,
6868
Address address,
69-
bool chargeForWarm = true)
69+
AccountAccessKind kind = AccountAccessKind.Default)
7070
{
71-
bool result = true;
72-
if (spec.UseHotAndColdStorage)
71+
if (!spec.UseHotAndColdStorage) return true;
72+
if (isTracingAccess)
7373
{
74-
if (isTracingAccess)
75-
{
76-
// Ensure that tracing simulates access-list behavior.
77-
accessTracker.WarmUp(address);
78-
}
74+
// Ensure that tracing simulates access-list behavior.
75+
accessTracker.WarmUp(address);
76+
}
7977

80-
// If the account is cold (and not a precompile), charge the cold access cost.
81-
if (!spec.IsPrecompile(address) && accessTracker.WarmUp(address))
82-
{
83-
result = UpdateGas(ref gas, GasCostOf.ColdAccountAccess);
84-
}
85-
else if (chargeForWarm)
78+
bool result;
79+
// If the account is cold (and not a precompile), charge the cold access cost.
80+
if (!spec.IsPrecompile(address) && accessTracker.WarmUp(address))
81+
result = UpdateGas(ref gas, GasCostOf.ColdAccountAccess);
82+
else
83+
{
84+
result = kind switch
8685
{
87-
// Otherwise, if warm access should be charged, apply the warm read cost.
88-
result = UpdateGas(ref gas, GasCostOf.WarmStateRead);
89-
}
86+
AccountAccessKind.SelfDestructBeneficiary => true, // no warm charge
87+
AccountAccessKind.Default => UpdateGas(ref gas, GasCostOf.WarmStateRead),
88+
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null)
89+
};
9090
}
9191

9292
return result;
9393
}
9494

95-
/// <summary>
96-
/// For Ethereum, SELFDESTRUCT beneficiary access is the same as regular account access.
97-
/// Arbitrum overrides this to charge cold access as full StorageAccess (no split).
98-
/// </summary>
99-
public static bool ConsumeSelfDestructBeneficiaryAccessGas(ref EthereumGasPolicy gas,
100-
IReleaseSpec spec,
101-
ref readonly StackAccessTracker accessTracker,
102-
bool isTracingAccess,
103-
Address address)
104-
{
105-
// For Ethereum, just use regular account access gas (no MultiGas tracking)
106-
return ConsumeAccountAccessGas(ref gas, spec, in accessTracker, isTracingAccess, address, chargeForWarm: false);
107-
}
108-
10995
public static bool ConsumeStorageAccessGas(ref EthereumGasPolicy gas,
11096
ref readonly StackAccessTracker accessTracker,
11197
bool isTracingAccess,

src/Nethermind/Nethermind.Evm/GasPolicy/IGasPolicy.cs

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,49 +81,33 @@ public interface IGasPolicy<TSelf> where TSelf : struct, IGasPolicy<TSelf>
8181
/// <param name="isTracingAccess">Whether access tracing is enabled.</param>
8282
/// <param name="address">The target account address.</param>
8383
/// <param name="delegated">The delegated account address, if any.</param>
84-
/// <param name="chargeForWarm">If true, charge even if the account is already warm.</param>
8584
/// <returns>True if gas was successfully charged; otherwise false.</returns>
8685
static abstract bool ConsumeAccountAccessGasWithDelegation(ref TSelf gas,
8786
IReleaseSpec spec,
8887
ref readonly StackAccessTracker accessTracker,
8988
bool isTracingAccess,
9089
Address address,
91-
Address? delegated,
92-
bool chargeForWarm = true);
90+
Address? delegated);
9391

9492
/// <summary>
9593
/// Charges gas for accessing an account based on its storage state (cold vs. warm).
9694
/// Precompiles are treated as exceptions to the cold/warm gas charge.
95+
/// The <paramref name="kind"/> discriminator controls the multi-gas resource split
96+
/// (e.g. SELFDESTRUCT beneficiary charges full cold cost to StorageAccess with no warm charge).
9797
/// </summary>
9898
/// <param name="gas">The gas state to update.</param>
9999
/// <param name="spec">The release specification governing gas costs.</param>
100100
/// <param name="accessTracker">The access tracker for cold/warm state.</param>
101101
/// <param name="isTracingAccess">Whether access tracing is enabled.</param>
102102
/// <param name="address">The target account address.</param>
103-
/// <param name="chargeForWarm">If true, applies the warm read gas cost even if the account is warm.</param>
103+
/// <param name="kind">Discriminator that varies resource-kind accounting per calling opcode.</param>
104104
/// <returns>True if the gas charge was successful; otherwise false.</returns>
105105
static abstract bool ConsumeAccountAccessGas(ref TSelf gas,
106106
IReleaseSpec spec,
107107
ref readonly StackAccessTracker accessTracker,
108108
bool isTracingAccess,
109109
Address address,
110-
bool chargeForWarm = true);
111-
112-
/// <summary>
113-
/// Charges gas for accessing the SELFDESTRUCT beneficiary account.
114-
/// For Arbitrum, cold access is charged as FULL StorageAccess (no Computation split).
115-
/// </summary>
116-
/// <param name="gas">The gas state to update.</param>
117-
/// <param name="spec">The release specification governing gas costs.</param>
118-
/// <param name="accessTracker">The access tracker for cold/warm state.</param>
119-
/// <param name="isTracingAccess">Whether access tracing is enabled.</param>
120-
/// <param name="address">The beneficiary account address.</param>
121-
/// <returns>True if the gas charge was successful; otherwise false.</returns>
122-
static abstract bool ConsumeSelfDestructBeneficiaryAccessGas(ref TSelf gas,
123-
IReleaseSpec spec,
124-
ref readonly StackAccessTracker accessTracker,
125-
bool isTracingAccess,
126-
Address address);
110+
AccountAccessKind kind = AccountAccessKind.Default);
127111

128112
/// <summary>
129113
/// Charges the appropriate gas cost for accessing a storage cell, taking into account whether the access is cold or warm.

src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,7 @@ private static EvmExceptionType InstructionSelfDestruct<TGasPolicy>(VirtualMachi
229229
goto StackUnderflow;
230230

231231
// Charge gas for SELFDESTRUCT beneficiary access; if insufficient, signal out-of-gas.
232-
// Uses ConsumeSelfDestructBeneficiaryAccessGas which charges cold access as full StorageAccess
233-
// (no Computation split for Arbitrum).
234-
if (!TGasPolicy.ConsumeSelfDestructBeneficiaryAccessGas(ref gas, spec, in vmState.AccessTracker, vm.TxTracer.IsTracingAccess, inheritor))
232+
if (!TGasPolicy.ConsumeAccountAccessGas(ref gas, spec, in vmState.AccessTracker, vm.TxTracer.IsTracingAccess, inheritor, AccountAccessKind.SelfDestructBeneficiary))
235233
goto OutOfGas;
236234

237235
Address executingAccount = vmState.Env.ExecutingAccount;

0 commit comments

Comments
 (0)