core, eth: rename regular gas to execution gas (#35457)

Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
This commit is contained in:
rjl493456442 2026-08-04 14:35:06 +08:00 committed by GitHub
parent b483fe9e71
commit 6434bc91d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 608 additions and 608 deletions

View file

@ -75,7 +75,7 @@ func TestEIP2780Intrinsic(t *testing.T) {
name: "contract creation, value = 0",
to: nil,
value: uint256.NewInt(0),
// TxBaseCost + CreateAccess = 23,000 regular. The new-account state
// TxBaseCost + CreateAccess = 23,000 execution. The new-account state
// charge depends on whether the deployment target exists and is
// charged at runtime, not intrinsically.
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam,
@ -84,7 +84,7 @@ func TestEIP2780Intrinsic(t *testing.T) {
name: "contract creation, value > 0",
to: nil,
value: uint256.NewInt(1),
// TxBaseCost + CreateAccess + TransferLogCost = 24,756 regular.
// TxBaseCost + CreateAccess = 24,756 execution.
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam,
},
{
@ -182,9 +182,9 @@ func TestEIP2780Gas(t *testing.T) {
}
cases := []struct {
name string
tx *types.Transaction
wantRegular, wantState uint64
name string
tx *types.Transaction
wantExecution, wantState uint64
}{
// case 1: ETH transfer to self.
{"self-transfer", callTx(0, senderAddr, 1, 100_000, nil), base, 0},
@ -216,8 +216,8 @@ func TestEIP2780Gas(t *testing.T) {
if res.Err != nil {
t.Fatalf("execution failed: %v", res.Err)
}
if gp.cumulativeRegular != tc.wantRegular {
t.Errorf("regular gas = %d, want %d", gp.cumulativeRegular, tc.wantRegular)
if gp.cumulativeExecution != tc.wantExecution {
t.Errorf("execution gas = %d, want %d", gp.cumulativeExecution, tc.wantExecution)
}
if gp.cumulativeState != tc.wantState {
t.Errorf("state gas = %d, want %d", gp.cumulativeState, tc.wantState)
@ -279,8 +279,8 @@ func TestEIP2780WarmRecipientStillChargedCold(t *testing.T) {
t.Fatalf("execution failed: %v", res.Err)
}
want := params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam + accessListEntryCost
if gp.cumulativeRegular != want {
t.Errorf("regular gas = %d, want %d (cold recipient, no access-list discount)", gp.cumulativeRegular, want)
if gp.cumulativeExecution != want {
t.Errorf("execution gas = %d, want %d (cold recipient, no access-list discount)", gp.cumulativeExecution, want)
}
}
@ -305,8 +305,8 @@ func TestEIP2780DelegatedWarmTarget(t *testing.T) {
}
want := params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam + accessListEntryCost + // recipient cold access (intrinsic)
params.WarmAccountAccessAmsterdam // warm delegation-target access (runtime)
if gp.cumulativeRegular != want {
t.Errorf("regular gas = %d, want %d (warm delegation target)", gp.cumulativeRegular, want)
if gp.cumulativeExecution != want {
t.Errorf("execution gas = %d, want %d (warm delegation target)", gp.cumulativeExecution, want)
}
}
@ -315,7 +315,7 @@ func TestEIP2780DelegatedWarmTarget(t *testing.T) {
// changes, including the already applied EIP-7702 delegations — while the
// sender's nonce increment persists.
//
// The halt burns the regular dimension in full; the state dimension is
// The halt burns the execution dimension in full; the state dimension is
// refilled by the revert and the reservoir — if any — is preserved and
// returned to the sender rather than burnt.
func TestEIP2780RuntimeOOGRevertsDelegations(t *testing.T) {
@ -323,7 +323,7 @@ func TestEIP2780RuntimeOOGRevertsDelegations(t *testing.T) {
name string
gas uint64
numAuths int
wantUsed uint64 // = gas reservoir: all regular burnt, reservoir returned
wantUsed uint64 // = gas reservoir: all execution burnt, reservoir returned
}{
// No state reservoir (gas below MaxTxGas). Gas covers the intrinsic
// cost (TX_BASE_COST + the cold-inclusive per-authorization base for
@ -333,7 +333,7 @@ func TestEIP2780RuntimeOOGRevertsDelegations(t *testing.T) {
// A 100,000 state reservoir (gas above MaxTxGas). The 100
// authorizations' state charges (~21.9M) overwhelm the reservoir and
// the regular budget they spill into. The reservoir is made whole by
// the execution budget they spill into. The reservoir is made whole by
// the halt-refill and returned to the sender.
{"with-reservoir", params.MaxTxGas + 100_000, 100, params.MaxTxGas},
}
@ -376,12 +376,12 @@ func TestEIP2780RuntimeOOGRevertsDelegations(t *testing.T) {
t.Fatalf("used gas = %d, want %d", res.UsedGas, tc.wantUsed)
}
// The charged state gas was refilled on the halt: the receipt is
// all regular, burnt in full, and only the reservoir survives.
// all execution, burnt in full, and only the reservoir survives.
if gp.cumulativeState != 0 {
t.Fatalf("state gas = %d, want 0 (refilled on halt)", gp.cumulativeState)
}
if gp.cumulativeRegular != tc.wantUsed {
t.Fatalf("regular gas = %d, want %d (burnt in full)", gp.cumulativeRegular, tc.wantUsed)
if gp.cumulativeExecution != tc.wantUsed {
t.Fatalf("execution gas = %d, want %d (burnt in full)", gp.cumulativeExecution, tc.wantUsed)
}
for i, authority := range authorities {
if code := sdb.GetCode(authority); len(code) != 0 {
@ -408,8 +408,8 @@ func TestEIP2780RecipientOOG(t *testing.T) {
// The reservoir case needs a near-cap intrinsic cost. This leaves just
// enough total budget for the authorization but not for the recipient leaf.
const (
regularLeft = 100_000
reservoir = 200_000
executionLeft = 100_000
reservoir = 200_000
)
al := types.AccessList{{Address: common.HexToAddress("0xa1")}}
baseIntrinsic, err := IntrinsicGas(nil, al, []types.SetCodeAuthorization{auth}, senderAddr, &recipient, uint256.NewInt(1), rules8037)
@ -417,7 +417,7 @@ func TestEIP2780RecipientOOG(t *testing.T) {
t.Fatal(err)
}
perKey := params.TxAccessListStorageKeyGasAmsterdam + uint64(common.HashLength)*params.TxCostFloorPerToken7976*params.TxTokenPerNonZeroByte
al[0].StorageKeys = make([]common.Hash, (params.MaxTxGas-regularLeft-baseIntrinsic)/perKey)
al[0].StorageKeys = make([]common.Hash, (params.MaxTxGas-executionLeft-baseIntrinsic)/perKey)
alIntrinsic, err := IntrinsicGas(nil, al, []types.SetCodeAuthorization{auth}, senderAddr, &recipient, uint256.NewInt(1), rules8037)
if err != nil {
t.Fatal(err)
@ -433,7 +433,7 @@ func TestEIP2780RecipientOOG(t *testing.T) {
// This exactly pays the first authorization, leaving no gas for the
// fresh recipient's account-leaf charge.
{"no-reservoir", setCodeTxGas(0, recipient, 1, intrinsic+params.AccountWriteAmsterdam+authWorstState, []types.SetCodeAuthorization{auth}), intrinsic + params.AccountWriteAmsterdam + authWorstState},
// The state reservoir is restored by the halt; only the capped regular
// The state reservoir is restored by the halt; only the capped execution
// dimension is burnt.
{"with-reservoir", setCodeTxGasAL(0, recipient, 1, params.MaxTxGas+reservoir, al, []types.SetCodeAuthorization{auth}), params.MaxTxGas},
}
@ -456,8 +456,8 @@ func TestEIP2780RecipientOOG(t *testing.T) {
if sdb.GetNonce(senderAddr) != 1 {
t.Fatal("sender nonce not consumed")
}
if res.UsedGas != tc.want || gp.cumulativeState != 0 || gp.cumulativeRegular != tc.want {
t.Fatalf("used/gas = %d/<%d,%d>, want %d/<%d,0>", res.UsedGas, gp.cumulativeRegular, gp.cumulativeState, tc.want, tc.want)
if res.UsedGas != tc.want || gp.cumulativeState != 0 || gp.cumulativeExecution != tc.want {
t.Fatalf("used/gas = %d/<%d,%d>, want %d/<%d,0>", res.UsedGas, gp.cumulativeExecution, gp.cumulativeState, tc.want, tc.want)
}
})
}
@ -479,8 +479,8 @@ func TestEIP2780SelfTransferDelegated(t *testing.T) {
t.Fatalf("execution failed: %v", res.Err)
}
want := params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam // base + cold delegation target
if gp.cumulativeRegular != want {
t.Errorf("regular gas = %d, want %d (base + delegation resolution)", gp.cumulativeRegular, want)
if gp.cumulativeExecution != want {
t.Errorf("execution gas = %d, want %d (base + delegation resolution)", gp.cumulativeExecution, want)
}
}
@ -542,27 +542,27 @@ func TestEIP2780RecipientKinds(t *testing.T) {
nonceOnly := common.HexToAddress("0xbeef000000000000000000000000000000000005")
precompile := common.BytesToAddress([]byte{4}) // identity; 15 gas for empty input
cases := []struct {
name string
alloc types.GenesisAlloc
tx *types.Transaction
wantRegular, wantState uint64
name string
alloc types.GenesisAlloc
tx *types.Transaction
wantExecution, wantState uint64
}{
{
name: "nonce-only",
alloc: types.GenesisAlloc{nonceOnly: {Nonce: 1}},
tx: callTx(0, nonceOnly, 1, 100_000, nil),
wantRegular: base + cold + valueCst,
name: "nonce-only",
alloc: types.GenesisAlloc{nonceOnly: {Nonce: 1}},
tx: callTx(0, nonceOnly, 1, 100_000, nil),
wantExecution: base + cold + valueCst,
},
{
name: "precompile/zero",
tx: callTx(0, precompile, 0, 100_000, nil),
wantRegular: base + cold + 15,
name: "precompile/zero",
tx: callTx(0, precompile, 0, 100_000, nil),
wantExecution: base + cold + 15,
},
{
name: "precompile/value",
tx: callTx(0, precompile, 1, 300_000, nil),
wantRegular: base + cold + valueCst + 15,
wantState: newAccountState,
name: "precompile/value",
tx: callTx(0, precompile, 1, 300_000, nil),
wantExecution: base + cold + valueCst + 15,
wantState: newAccountState,
},
}
for _, tc := range cases {
@ -571,8 +571,8 @@ func TestEIP2780RecipientKinds(t *testing.T) {
if err != nil || res.Err != nil {
t.Fatalf("result=%v err=%v", res, err)
}
if gp.cumulativeRegular != tc.wantRegular || gp.cumulativeState != tc.wantState {
t.Fatalf("gas = <%d,%d>, want <%d,%d>", gp.cumulativeRegular, gp.cumulativeState, tc.wantRegular, tc.wantState)
if gp.cumulativeExecution != tc.wantExecution || gp.cumulativeState != tc.wantState {
t.Fatalf("gas = <%d,%d>, want <%d,%d>", gp.cumulativeExecution, gp.cumulativeState, tc.wantExecution, tc.wantState)
}
})
}
@ -590,8 +590,8 @@ func TestEIP2780RecipientRefill(t *testing.T) {
if err != nil || res.Err == nil {
t.Fatalf("result=%v err=%v, want exceptional halt", res, err)
}
if gp.cumulativeState != 0 || gp.cumulativeRegular != params.MaxTxGas {
t.Fatalf("gas = <%d,%d>, want <%d,0> after refill", gp.cumulativeRegular, gp.cumulativeState, params.MaxTxGas)
if gp.cumulativeState != 0 || gp.cumulativeExecution != params.MaxTxGas {
t.Fatalf("gas = <%d,%d>, want <%d,0> after refill", gp.cumulativeExecution, gp.cumulativeState, params.MaxTxGas)
}
if sdb.Exist(recipient) {
t.Fatal("empty recipient persisted after halted dispatch")
@ -608,8 +608,8 @@ func TestEIP2780Coinbase(t *testing.T) {
if err != nil || res.Err != nil {
t.Fatalf("result=%v err=%v", res, err)
}
if want := params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam; gp.cumulativeRegular != want {
t.Fatalf("regular gas = %d, want %d", gp.cumulativeRegular, want)
if want := params.TxBaseCost2780 + params.ColdAccountAccessAmsterdam; gp.cumulativeExecution != want {
t.Fatalf("execution gas = %d, want %d", gp.cumulativeExecution, want)
}
}
@ -624,10 +624,10 @@ func TestEIP2780DelegationWarmth(t *testing.T) {
recipient := common.HexToAddress("0xde1e000000000000000000000000000000000008")
precompile := common.BytesToAddress([]byte{4})
cases := []struct {
name string
target common.Address
coinbase common.Address
wantRegular uint64
name string
target common.Address
coinbase common.Address
wantExecution uint64
}{
{"precompile", precompile, common.Address{}, base + cold + warm},
{"coinbase", common.HexToAddress("0xc01ba5e000000000000000000000000000000002"), common.HexToAddress("0xc01ba5e000000000000000000000000000000002"), base + cold + warm},
@ -641,8 +641,8 @@ func TestEIP2780DelegationWarmth(t *testing.T) {
if err != nil || res.Err != nil {
t.Fatalf("result=%v err=%v", res, err)
}
if gp.cumulativeRegular != tc.wantRegular {
t.Fatalf("regular gas = %d, want %d", gp.cumulativeRegular, tc.wantRegular)
if gp.cumulativeExecution != tc.wantExecution {
t.Fatalf("execution gas = %d, want %d", gp.cumulativeExecution, tc.wantExecution)
}
})
}
@ -656,8 +656,8 @@ func TestEIP2780DelegationWarmth(t *testing.T) {
st := newStateTransition(amsterdamCoreEVM(sdb), &Message{To: &to, Value: new(uint256.Int)}, NewGasPool(100_000))
st.gasRemaining = vm.NewGasBudget(1_000, 0)
sdb.AddAddressToAccessList(recipient)
if !st.chargeCallRecipientEIP2780(new(uint256.Int)) || st.gasRemaining.UsedRegularGas != warm {
t.Fatalf("recipient target charge = %d, want warm %d", st.gasRemaining.UsedRegularGas, warm)
if !st.chargeCallRecipientEIP2780(new(uint256.Int)) || st.gasRemaining.UsedExecutionGas != warm {
t.Fatalf("recipient target charge = %d, want warm %d", st.gasRemaining.UsedExecutionGas, warm)
}
}
@ -678,40 +678,40 @@ func TestEIP2780InstallDispatch(t *testing.T) {
t.Fatal(err)
}
cases := []struct {
name string
alloc types.GenesisAlloc
tx *types.Transaction
account common.Address
wantRegular, wantState uint64
wantNonce uint64
wantBalance *big.Int
name string
alloc types.GenesisAlloc
tx *types.Transaction
account common.Address
wantExecution, wantState uint64
wantNonce uint64
wantBalance *big.Int
}{
{
name: "sender",
tx: setCodeTxGas(0, senderAddr, 0, 1_000_000, []types.SetCodeAuthorization{senderAuth}),
account: senderAddr,
wantRegular: base + perAuth + cold,
wantState: authBaseState,
wantNonce: 2,
name: "sender",
tx: setCodeTxGas(0, senderAddr, 0, 1_000_000, []types.SetCodeAuthorization{senderAuth}),
account: senderAddr,
wantExecution: base + perAuth + cold,
wantState: authBaseState,
wantNonce: 2,
},
{
name: "fresh-recipient",
tx: setCodeTxGas(0, authority, 1, 1_000_000, []types.SetCodeAuthorization{auth}),
account: authority,
wantRegular: base + cold + valueCst + perAuth + cold,
wantState: authWorstState,
wantNonce: 1,
wantBalance: big.NewInt(1),
name: "fresh-recipient",
tx: setCodeTxGas(0, authority, 1, 1_000_000, []types.SetCodeAuthorization{auth}),
account: authority,
wantExecution: base + cold + valueCst + perAuth + cold,
wantState: authWorstState,
wantNonce: 1,
wantBalance: big.NewInt(1),
},
{
name: "funded-recipient",
alloc: types.GenesisAlloc{authority: {Balance: big.NewInt(3)}},
tx: setCodeTxGas(0, authority, 1, 1_000_000, []types.SetCodeAuthorization{auth}),
account: authority,
wantRegular: base + cold + valueCst + perAuth + cold,
wantState: authBaseState,
wantNonce: 1,
wantBalance: big.NewInt(4),
name: "funded-recipient",
alloc: types.GenesisAlloc{authority: {Balance: big.NewInt(3)}},
tx: setCodeTxGas(0, authority, 1, 1_000_000, []types.SetCodeAuthorization{auth}),
account: authority,
wantExecution: base + cold + valueCst + perAuth + cold,
wantState: authBaseState,
wantNonce: 1,
wantBalance: big.NewInt(4),
},
}
for _, tc := range cases {
@ -727,14 +727,14 @@ func TestEIP2780InstallDispatch(t *testing.T) {
if tc.wantBalance != nil && sdb.GetBalance(tc.account).Cmp(uint256.MustFromBig(tc.wantBalance)) != 0 {
t.Fatalf("balance = %v, want %v", sdb.GetBalance(tc.account), tc.wantBalance)
}
if gp.cumulativeRegular != tc.wantRegular || gp.cumulativeState != tc.wantState {
t.Fatalf("gas = <%d,%d>, want <%d,%d>", gp.cumulativeRegular, gp.cumulativeState, tc.wantRegular, tc.wantState)
if gp.cumulativeExecution != tc.wantExecution || gp.cumulativeState != tc.wantState {
t.Fatalf("gas = <%d,%d>, want <%d,%d>", gp.cumulativeExecution, gp.cumulativeState, tc.wantExecution, tc.wantState)
}
})
}
}
// TestEIP2780Floor keeps the EIP-8037 calldata floor in the regular dimension
// TestEIP2780Floor keeps the EIP-8037 calldata floor in the execution dimension
// when a top-level EIP-2780 account-leaf charge is also present.
func TestEIP2780Floor(t *testing.T) {
recipient := common.HexToAddress("0xbeef000000000000000000000000000000000007")
@ -753,14 +753,14 @@ func TestEIP2780Floor(t *testing.T) {
t.Fatal(err)
}
stateGas := newAccountState
// This is the v7.2.0 boundary: the floor lifts only the regular
// This is the v7.2.0 boundary: the floor lifts only the execution
// dimension, while the scalar receipt gas remains the actual intrinsic +
// state charge because it is already above the floor.
if !(intrinsic < floor && floor < intrinsic+stateGas) {
t.Fatalf("expected intrinsic < floor < intrinsic + state: %d < %d < %d", intrinsic, floor, intrinsic+stateGas)
}
if gp.cumulativeRegular != floor || gp.cumulativeState != stateGas {
t.Fatalf("gas = <%d,%d>, want floor/state <%d,%d>", gp.cumulativeRegular, gp.cumulativeState, floor, stateGas)
if gp.cumulativeExecution != floor || gp.cumulativeState != stateGas {
t.Fatalf("gas = <%d,%d>, want floor/state <%d,%d>", gp.cumulativeExecution, gp.cumulativeState, floor, stateGas)
}
if want := intrinsic + stateGas; res.UsedGas != want {
t.Fatalf("receipt gas = %d, want intrinsic + state = %d", res.UsedGas, want)
@ -775,22 +775,22 @@ func TestEIP2780Floor(t *testing.T) {
// with their state-gas charge (the EIP-7702 delegations of a call tx);
// - state gas pre-charged for the frame itself is refilled when the halt
// voids it (the account-creation charge of a creation tx);
// - after the refill the regular dimension is burnt in full, while any
// - after the refill the execution dimension is burnt in full, while any
// remaining state reservoir is preserved and returned to the sender.
func TestEIP2780FirstFrameHaltPreservesPreExecution(t *testing.T) {
halting := common.HexToAddress("0xbad0000000000000000000000000000000000002")
cases := []struct {
name string
create bool
gas uint64
wantUsed uint64 // = gas preserved reservoir
wantRegular uint64
wantState uint64
name string
create bool
gas uint64
wantUsed uint64 // = gas preserved reservoir
wantExecution uint64
wantState uint64
}{
// Message call carrying one authorization: the delegation and its
// state charge (account + indicator) survive the halt.
//
// Without a reservoir the charge spills from regular gas and everything is
// Without a reservoir the charge spills from execution gas and everything is
// burnt;
//
// With a reservoir, the reservoir remainder is preserved.
@ -801,7 +801,7 @@ func TestEIP2780FirstFrameHaltPreservesPreExecution(t *testing.T) {
// the pre-charged account creation is refilled and no state gas
// remains.
//
// Without a reservoir the refill repays spilled regular gas, which the
// Without a reservoir the refill repays spilled execution gas, which the
// halt then burns along with the rest;
//
// With a reservoir, the refill makes the reservoir whole again and it
@ -845,8 +845,8 @@ func TestEIP2780FirstFrameHaltPreservesPreExecution(t *testing.T) {
if res.UsedGas != tc.wantUsed {
t.Fatalf("used gas = %d, want %d", res.UsedGas, tc.wantUsed)
}
if gp.cumulativeRegular != tc.wantRegular {
t.Fatalf("regular gas = %d, want %d (burnt in full)", gp.cumulativeRegular, tc.wantRegular)
if gp.cumulativeExecution != tc.wantExecution {
t.Fatalf("execution gas = %d, want %d (burnt in full)", gp.cumulativeExecution, tc.wantExecution)
}
if gp.cumulativeState != tc.wantState {
t.Fatalf("state gas = %d, want %d", gp.cumulativeState, tc.wantState)
@ -876,15 +876,15 @@ func TestEIP2780FirstFrameHaltPreservesPreExecution(t *testing.T) {
// TestEIP2780CreatePreExecutionOOGPreservesReservoir verifies that when a
// creation transaction cannot afford the pre-execution account-creation state
// charge (before the init-code frame is entered), the transaction halts with
// all regular gas burnt while the state reservoir — never touched, since the
// all execution gas burnt while the state reservoir — never touched, since the
// charge is atomic and was not applied — is preserved and returned to the
// sender.
func TestEIP2780CreatePreExecutionOOGPreservesReservoir(t *testing.T) {
// Regular gas left for the pre-execution charge; together with the
// Execution gas left for the pre-execution charge; together with the
// reservoir it must not cover the account-creation cost.
const (
regularLeft = 100_000
reservoir = 50_000
executionLeft = 100_000
reservoir = 50_000
)
// Plain creation intrinsic: TX_BASE_COST + CREATE_ACCESS.
plainIntrinsic, err := IntrinsicGas(nil, nil, nil, senderAddr, nil, new(uint256.Int), rules8037)
@ -892,9 +892,9 @@ func TestEIP2780CreatePreExecutionOOGPreservesReservoir(t *testing.T) {
t.Fatal(err)
}
// For the reservoir case the gas limit must exceed MaxTxGas, which leaves
// a huge regular budget by default. A big access list drives the intrinsic
// cost close to MaxTxGas, shrinking the regular budget back down to
// roughly regularLeft. Storage keys work because their intrinsic charge
// a huge execution budget by default. A big access list drives the intrinsic
// cost close to MaxTxGas, shrinking the execution budget back down to
// roughly executionLeft. Storage keys work because their intrinsic charge
// exceeds their EIP-7623/7976 floor contribution.
al := types.AccessList{{Address: common.HexToAddress("0xa1")}}
baseIntrinsic, err := IntrinsicGas(nil, al, nil, senderAddr, nil, new(uint256.Int), rules8037)
@ -905,13 +905,13 @@ func TestEIP2780CreatePreExecutionOOGPreservesReservoir(t *testing.T) {
// Fill the transaction with accessList, drain the gas and make it
// insufficient for account-creation cost.
al[0].StorageKeys = make([]common.Hash, (params.MaxTxGas-regularLeft-baseIntrinsic)/perKey)
al[0].StorageKeys = make([]common.Hash, (params.MaxTxGas-executionLeft-baseIntrinsic)/perKey)
alIntrinsic, err := IntrinsicGas(nil, al, nil, senderAddr, nil, new(uint256.Int), rules8037)
if err != nil {
t.Fatal(err)
}
if left := params.MaxTxGas - alIntrinsic; left+reservoir >= newAccountState {
t.Fatalf("setup: regular %d + reservoir %d must not cover the creation charge %d", left, reservoir, newAccountState)
t.Fatalf("setup: execution %d + reservoir %d must not cover the creation charge %d", left, reservoir, newAccountState)
}
alCreateTx := types.MustSignNewTx(senderKey, signer8037,
&types.DynamicFeeTx{
@ -931,7 +931,7 @@ func TestEIP2780CreatePreExecutionOOGPreservesReservoir(t *testing.T) {
wantUsed uint64 // = gas preserved reservoir
}{
// Gas below MaxTxGas: no reservoir, the whole limit is burnt.
{"no-reservoir", createTx(0, plainIntrinsic+regularLeft, nil), plainIntrinsic + regularLeft},
{"no-reservoir", createTx(0, plainIntrinsic+executionLeft, nil), plainIntrinsic + executionLeft},
// Gas above MaxTxGas: the reservoir survives the halt untouched and
// is returned to the sender.
@ -950,8 +950,8 @@ func TestEIP2780CreatePreExecutionOOGPreservesReservoir(t *testing.T) {
if res.UsedGas != tc.wantUsed {
t.Fatalf("used gas = %d, want %d", res.UsedGas, tc.wantUsed)
}
if gp.cumulativeRegular != tc.wantUsed {
t.Fatalf("regular gas = %d, want %d (burnt in full)", gp.cumulativeRegular, tc.wantUsed)
if gp.cumulativeExecution != tc.wantUsed {
t.Fatalf("execution gas = %d, want %d (burnt in full)", gp.cumulativeExecution, tc.wantUsed)
}
if gp.cumulativeState != 0 {
t.Fatalf("state gas = %d, want 0 (charge never applied)", gp.cumulativeState)
@ -1005,82 +1005,82 @@ func TestEIP2780AuthorityAccountWrite(t *testing.T) {
fundedAuthority := types.GenesisAlloc{authority: {Balance: big.NewInt(1)}}
cases := []struct {
name string
alloc types.GenesisAlloc
tx *types.Transaction
wantRegular, wantState uint64
name string
alloc types.GenesisAlloc
tx *types.Transaction
wantExecution, wantState uint64
}{
{
// Materializing a fresh authority pays the first-write surcharge
// alongside the new-account state gas and the indicator bytes.
name: "fresh authority",
tx: tx(existingEOA, 0, auth0),
wantRegular: base + cold + perAuth + aw,
wantState: authWorstState,
name: "fresh authority",
tx: tx(existingEOA, 0, auth0),
wantExecution: base + cold + perAuth + aw,
wantState: authWorstState,
},
{
// An existing authority still pays the surcharge: the nonce and
// indicator stores are the first write to the account within the
// transaction.
name: "existing authority",
alloc: fundedAuthority,
tx: tx(existingEOA, 0, auth0),
wantRegular: base + cold + perAuth + aw,
wantState: authBaseState,
name: "existing authority",
alloc: fundedAuthority,
tx: tx(existingEOA, 0, auth0),
wantExecution: base + cold + perAuth + aw,
wantState: authBaseState,
},
{
// Self-sponsored: the sender's account write is prepaid by
// TX_BASE_COST, no surcharge.
name: "authority is sender",
tx: tx(existingEOA, 0, senderAuth),
wantRegular: base + cold + perAuth,
wantState: authBaseState,
name: "authority is sender",
tx: tx(existingEOA, 0, senderAuth),
wantExecution: base + cold + perAuth,
wantState: authBaseState,
},
{
// authority == tx.to with zero value: no TX_VALUE_COST was paid,
// so the authorization write is the first paid write and the
// surcharge applies. The recipient becomes delegated, adding a
// cold delegation-target access at runtime.
name: "authority is recipient, zero value",
alloc: fundedAuthority,
tx: tx(authority, 0, auth0),
wantRegular: base + cold + perAuth + aw + cold,
wantState: authBaseState,
name: "authority is recipient, zero value",
alloc: fundedAuthority,
tx: tx(authority, 0, auth0),
wantExecution: base + cold + perAuth + aw + cold,
wantState: authBaseState,
},
{
// authority == tx.to with value: TX_VALUE_COST prepaid the
// recipient write, so no surcharge is due.
name: "authority is recipient, value",
alloc: fundedAuthority,
tx: tx(authority, 1, auth0),
wantRegular: base + cold + valueCst + perAuth + cold,
wantState: authBaseState,
name: "authority is recipient, value",
alloc: fundedAuthority,
tx: tx(authority, 1, auth0),
wantExecution: base + cold + valueCst + perAuth + cold,
wantState: authBaseState,
},
{
// Fresh authority == tx.to with value: the authorization pays the
// new-account state gas, and the recipient charge then sees an
// existing account, so the leaf is not paid for twice.
name: "authority is fresh recipient, value",
tx: tx(authority, 1, auth0),
wantRegular: base + cold + valueCst + perAuth + cold,
wantState: authWorstState,
name: "authority is fresh recipient, value",
tx: tx(authority, 1, auth0),
wantExecution: base + cold + valueCst + perAuth + cold,
wantState: authWorstState,
},
{
// The same authority twice: only the first valid authorization
// carries the surcharge, the account creation and the indicator.
name: "same authority twice",
tx: tx(existingEOA, 0, auth0, auth1),
wantRegular: base + cold + 2*perAuth + aw,
wantState: authWorstState,
name: "same authority twice",
tx: tx(existingEOA, 0, auth0, auth1),
wantExecution: base + cold + 2*perAuth + aw,
wantState: authWorstState,
},
{
// An invalid authorization performs no write and does not count
// as the first write; the following valid one pays in full. The
// per-auth intrinsic base is still paid for the invalid tuple.
name: "invalid then valid",
tx: tx(existingEOA, 0, authBadNonce, auth0),
wantRegular: base + cold + 2*perAuth + aw,
wantState: authWorstState,
name: "invalid then valid",
tx: tx(existingEOA, 0, authBadNonce, auth0),
wantExecution: base + cold + 2*perAuth + aw,
wantState: authWorstState,
},
}
for _, tc := range cases {
@ -1096,8 +1096,8 @@ func TestEIP2780AuthorityAccountWrite(t *testing.T) {
if res.Err != nil {
t.Fatalf("execution failed: %v", res.Err)
}
if gp.cumulativeRegular != tc.wantRegular {
t.Errorf("regular gas = %d, want %d", gp.cumulativeRegular, tc.wantRegular)
if gp.cumulativeExecution != tc.wantExecution {
t.Errorf("execution gas = %d, want %d", gp.cumulativeExecution, tc.wantExecution)
}
if gp.cumulativeState != tc.wantState {
t.Errorf("state gas = %d, want %d", gp.cumulativeState, tc.wantState)
@ -1130,8 +1130,8 @@ func TestEIP2780DelegationTargetPrewarmed(t *testing.T) {
if res.Err != nil {
t.Fatalf("execution failed: %v", res.Err)
}
if want := base + cold + warm; gp.cumulativeRegular != want {
t.Errorf("regular gas = %d, want %d (warm delegation target)", gp.cumulativeRegular, want)
if want := base + cold + warm; gp.cumulativeExecution != want {
t.Errorf("execution gas = %d, want %d (warm delegation target)", gp.cumulativeExecution, want)
}
if gp.cumulativeState != 0 {
t.Errorf("state gas = %d, want 0", gp.cumulativeState)
@ -1156,8 +1156,8 @@ func TestEIP2780DelegationTargetPrewarmed(t *testing.T) {
if res.Err != nil {
t.Fatalf("execution failed: %v", res.Err)
}
if want := base + cold + perAuth + aw + warm; gp.cumulativeRegular != want {
t.Errorf("regular gas = %d, want %d (auth-warmed delegation target)", gp.cumulativeRegular, want)
if want := base + cold + perAuth + aw + warm; gp.cumulativeExecution != want {
t.Errorf("execution gas = %d, want %d (auth-warmed delegation target)", gp.cumulativeExecution, want)
}
if gp.cumulativeState != newAccountState {
t.Errorf("state gas = %d, want %d (authority account created)", gp.cumulativeState, newAccountState)

View file

@ -1045,7 +1045,7 @@ func TestBALInEVMCreatePreAccessAbortDestinationExcluded(t *testing.T) {
func TestBALInEVMCreateOOGDestination(t *testing.T) {
factory := common.HexToAddress("0xfac4")
// PUSH1 0 (length) PUSH1 0 (offset) PUSH1 0 (value) CREATE POP STOP.
// The factory has enough regular gas for CREATE's opcode cost but not enough
// The factory has enough execution gas for CREATE's opcode cost but not enough
// combined gas to pay Amsterdam's 183,600 account-creation state charge.
code := []byte{0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xf0, 0x50, 0x00}
env := newBALTestEnv(types.GenesisAlloc{

View file

@ -16,7 +16,7 @@
// Transaction- and block-level tests for EIP-8037 (multidimensional state-gas
// metering). They apply whole transactions and inspect the 2D block gas pool
// (cumulativeRegular / cumulativeState) and the receipt/peak figures.
// (cumulativeExecution / cumulativeState) and the receipt/peak figures.
package core
@ -165,32 +165,32 @@ func applyMsg(t *testing.T, sdb *state.StateDB, tx *types.Transaction) (*Executi
// assertBudgetSane validates the final tx-level GasBudget vector:
//
// regular: RegularGas + UsedRegularGas + Spilled == initial.RegularGas
// state: StateGas + UsedStateGas == initial.StateGas + Spilled
// scalar: Used(initial) == UsedRegularGas + UsedStateGas
// execution: ExecutionGas + UsedExecutionGas + Spilled == initial.ExecutionGas
// state: StateGas + UsedStateGas == initial.StateGas + Spilled
// scalar: Used(initial) == UsedExecutionGas + UsedStateGas
func assertBudgetSane(t *testing.T, initial, got vm.GasBudget) {
t.Helper()
if got.RegularGas+got.UsedRegularGas+got.Spilled != initial.RegularGas {
t.Fatalf("regular not conserved: R=%d usedR=%d spilled=%d, want sum %d",
got.RegularGas, got.UsedRegularGas, got.Spilled, initial.RegularGas)
if got.ExecutionGas+got.UsedExecutionGas+got.Spilled != initial.ExecutionGas {
t.Fatalf("execution not conserved: R=%d usedR=%d spilled=%d, want sum %d",
got.ExecutionGas, got.UsedExecutionGas, got.Spilled, initial.ExecutionGas)
}
if int64(got.StateGas)+got.UsedStateGas != int64(initial.StateGas)+int64(got.Spilled) {
t.Fatalf("state not conserved: S=%d usedS=%d spilled=%d, want %d+spilled",
got.StateGas, got.UsedStateGas, got.Spilled, initial.StateGas)
}
if int64(got.Used(initial)) != int64(got.UsedRegularGas)+got.UsedStateGas {
if int64(got.Used(initial)) != int64(got.UsedExecutionGas)+got.UsedStateGas {
t.Fatalf("scalar mismatch: used=%d, usedR=%d usedS=%d",
got.Used(initial), got.UsedRegularGas, got.UsedStateGas)
got.Used(initial), got.UsedExecutionGas, got.UsedStateGas)
}
}
// assertPoolSane validates the whole 2D block-gas-pool vector after a single tx.
//
// receipt: cumulativeUsed == res.UsedGas <= res.MaxUsedGas
// regular: cumulativeRegular <= max(res.MaxUsedGas - cumulativeState, floor)
// (the calldata floor pads the regular dimension alone, so the
// execution: cumulativeExecution <= max(res.MaxUsedGas - cumulativeState, floor)
// (the calldata floor pads the execution dimension alone, so the
// dimension sum may exceed the pre-refund peak when it binds)
// bottleneck: Used() == max(cumulativeRegular, cumulativeState) <= initial
// bottleneck: Used() == max(cumulativeExecution, cumulativeState) <= initial
func assertPoolSane(t *testing.T, res *ExecutionResult, gp *GasPool, floor uint64) {
t.Helper()
if gp.cumulativeUsed != res.UsedGas {
@ -199,18 +199,18 @@ func assertPoolSane(t *testing.T, res *ExecutionResult, gp *GasPool, floor uint6
if res.UsedGas > res.MaxUsedGas {
t.Fatalf("post-refund gas %d exceeds peak %d", res.UsedGas, res.MaxUsedGas)
}
if gp.cumulativeRegular > res.MaxUsedGas {
t.Fatalf("regular %d exceeds peak %d", gp.cumulativeRegular, res.MaxUsedGas)
if gp.cumulativeExecution > res.MaxUsedGas {
t.Fatalf("execution %d exceeds peak %d", gp.cumulativeExecution, res.MaxUsedGas)
}
if gp.cumulativeState > res.MaxUsedGas {
t.Fatalf("state %d exceeds peak %d", gp.cumulativeState, res.MaxUsedGas)
}
if cap := max(res.MaxUsedGas-gp.cumulativeState, floor); gp.cumulativeRegular > cap {
t.Fatalf("regular %d exceeds pre-refund cap %d (peak %d, state %d, floor %d)",
gp.cumulativeRegular, cap, res.MaxUsedGas, gp.cumulativeState, floor)
if cap := max(res.MaxUsedGas-gp.cumulativeState, floor); gp.cumulativeExecution > cap {
t.Fatalf("execution %d exceeds pre-refund cap %d (peak %d, state %d, floor %d)",
gp.cumulativeExecution, cap, res.MaxUsedGas, gp.cumulativeState, floor)
}
if gp.Used() != max(gp.cumulativeRegular, gp.cumulativeState) {
t.Fatalf("block used %d != max(%d,%d)", gp.Used(), gp.cumulativeRegular, gp.cumulativeState)
if gp.Used() != max(gp.cumulativeExecution, gp.cumulativeState) {
t.Fatalf("block used %d != max(%d,%d)", gp.Used(), gp.cumulativeExecution, gp.cumulativeState)
}
if gp.Used() > gp.initial {
t.Fatalf("block used %d exceeds limit %d", gp.Used(), gp.initial)
@ -310,9 +310,9 @@ func TestCreateTxCollisionConsumesGasLeft(t *testing.T) {
t.Fatalf("state gas = %d, want 0 (never charged)", gp.cumulativeState)
}
// All forwarded gas_left is burned: the whole gas limit is consumed as
// regular gas.
if want := uint64(gas); gp.cumulativeRegular != want {
t.Fatalf("regular gas = %d, want %d", gp.cumulativeRegular, want)
// execution gas.
if want := uint64(gas); gp.cumulativeExecution != want {
t.Fatalf("execution gas = %d, want %d", gp.cumulativeExecution, want)
}
}
@ -434,7 +434,7 @@ func TestCreate2StorageOnlyDestCharged(t *testing.T) {
}
// If the pre-charge succeeds and the create frame then fails, only the create
// frame halts: the forwarded regular gas is burnt, the account-creation
// frame halts: the forwarded execution gas is burnt, the account-creation
// charge is refilled, and the parent frame continues.
func TestCreate2StorageOnlyDestRefillOnFrameHalt(t *testing.T) {
const gas = 1_000_000
@ -483,8 +483,8 @@ func TestCreate2StorageOnlyDestPrechargeOOG(t *testing.T) {
t.Fatalf("state gas = %d, want 0 (charge never applied)", gp.cumulativeState)
}
// The parent is the topmost frame, so its halt burns the whole gas limit.
if gp.cumulativeRegular != gas {
t.Fatalf("regular gas = %d, want %d", gp.cumulativeRegular, gas)
if gp.cumulativeExecution != gas {
t.Fatalf("execution gas = %d, want %d", gp.cumulativeExecution, gas)
}
}
@ -548,15 +548,15 @@ func TestPrechargeOOGEmitsTopFrame(t *testing.T) {
// ======================== Transaction validation =========================
// The regular dimension must have room for min(tx.gas, MaxTxGas).
func TestValidationRegularGasAvailable(t *testing.T) {
// The execution dimension must have room for min(tx.gas, MaxTxGas).
func TestValidationExecutionGasAvailable(t *testing.T) {
gp := NewGasPool(30_000_000)
gp.cumulativeRegular = 29_000_000
gp.cumulativeExecution = 29_000_000
if gp.CheckGasAmsterdam(2_000_000, 0) == nil {
t.Fatal("expected regular dimension full")
t.Fatal("expected execution dimension full")
}
if err := gp.CheckGasAmsterdam(1_000_000, 0); err != nil {
t.Fatalf("regular fits but rejected: %v", err)
t.Fatalf("execution fits but rejected: %v", err)
}
}
@ -572,7 +572,7 @@ func TestValidationStateGasAvailable(t *testing.T) {
}
}
// tx.gas may exceed MaxTxGas: regular is capped at MaxTxGas while the state
// tx.gas may exceed MaxTxGas: execution is capped at MaxTxGas while the state
// dimension reserves the full tx.gas (the excess lands in the reservoir).
func TestValidationStateGasOverflowAllowed(t *testing.T) {
gas := params.MaxTxGas + 5_000_000
@ -588,9 +588,9 @@ func TestValidationStateGasOverflowAllowed(t *testing.T) {
}
}
// Intrinsic regular gas above MaxTxGas (EIP-7825 cap) is rejected.
func TestValidationIntrinsicRegularCap(t *testing.T) {
al := make(types.AccessList, 8000) // ~19.2M regular, over the 16.77M cap
// Intrinsic execution gas above MaxTxGas (EIP-7825 cap) is rejected.
func TestValidationIntrinsicExecutionCap(t *testing.T) {
al := make(types.AccessList, 8000) // ~19.2M execution, over the 16.77M cap
for i := range al {
al[i].Address = common.BigToAddress(big.NewInt(int64(i + 1)))
}
@ -606,7 +606,7 @@ func TestValidationIntrinsicRegularCap(t *testing.T) {
AccessList: al,
})
if _, _, err := applyMsg(t, mkState(senderAlloc(nil)), tx); err == nil {
t.Fatal("expected rejection for intrinsic regular over MaxTxGas")
t.Fatal("expected rejection for intrinsic execution over MaxTxGas")
}
}
@ -708,14 +708,14 @@ func TestRefundFloorNegatesRefund(t *testing.T) {
// ========================= Block-level accounting ========================
// The pool tracks regular and state cumulatively in separate counters.
// The pool tracks execution and state cumulatively in separate counters.
func TestBlockTracksTwoCounters(t *testing.T) {
gp := NewGasPool(60_000_000)
if err := gp.ChargeGasAmsterdam(100, 200, 300); err != nil {
t.Fatal(err)
}
if gp.cumulativeRegular != 100 || gp.cumulativeState != 200 {
t.Fatalf("counters = (%d,%d), want (100,200)", gp.cumulativeRegular, gp.cumulativeState)
if gp.cumulativeExecution != 100 || gp.cumulativeState != 200 {
t.Fatalf("counters = (%d,%d), want (100,200)", gp.cumulativeExecution, gp.cumulativeState)
}
}
@ -731,7 +731,7 @@ func TestBlockGasUsedIsMax(t *testing.T) {
// Block validity is checked against the max dimension, not the sum.
func TestBlockValidityAgainstMax(t *testing.T) {
gp := NewGasPool(150)
// regular 100 + state 120: sum 220 > 150 but max 120 <= 150 is valid.
// execution 100 + state 120: sum 220 > 150 but max 120 <= 150 is valid.
if err := gp.ChargeGasAmsterdam(100, 120, 0); err != nil {
t.Fatalf("max within limit but rejected: %v", err)
}
@ -943,11 +943,11 @@ func TestAuthDuplicateAuthorityOnce(t *testing.T) {
// ===================== System contracts / system calls ===================
// System call gas limit keeps 30M regular plus a state reservoir for new slots.
// System call gas limit keeps 30M execution plus a state reservoir for new slots.
func TestSystemCallGasLimit(t *testing.T) {
limit, budget := systemCallGasBudget(amsterdamCoreEVM(mkState(nil)))
if limit != 30_000_000 || budget.RegularGas != 30_000_000 {
t.Fatalf("limit/regular = %d/%d, want 30M/30M", limit, budget.RegularGas)
if limit != 30_000_000 || budget.ExecutionGas != 30_000_000 {
t.Fatalf("limit/execution = %d/%d, want 30M/30M", limit, budget.ExecutionGas)
}
}

View file

@ -38,7 +38,7 @@ func newAuthTestTransition(sdb *state.StateDB) *stateTransition {
}
// A net-new delegation on a fresh, cold authority is charged ACCOUNT_WRITE in
// regular gas (the authority's cold access is paid unconditionally at the
// execution gas (the authority's cold access is paid unconditionally at the
// intrinsic phase, not here), plus the account leaf and the indicator bytes in
// state gas.
func TestAuthRuntimeChargeNetNew(t *testing.T) {
@ -47,8 +47,8 @@ func TestAuthRuntimeChargeNetNew(t *testing.T) {
if err := st.applyAuthorization(rules8037, &auth, map[common.Address]*authTracking{}); err != nil {
t.Fatal(err)
}
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
t.Fatalf("regular charged = %d, want %d", st.gasRemaining.UsedRegularGas, want)
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedExecutionGas != want {
t.Fatalf("execution charged = %d, want %d", st.gasRemaining.UsedExecutionGas, want)
}
if want := int64(authWorstState); st.gasRemaining.UsedStateGas != want {
t.Fatalf("state charged = %d, want %d", st.gasRemaining.UsedStateGas, want)
@ -65,8 +65,8 @@ func TestAuthRuntimeChargeExistingAccount(t *testing.T) {
if err := st.applyAuthorization(rules8037, &auth, map[common.Address]*authTracking{}); err != nil {
t.Fatal(err)
}
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
t.Fatalf("regular charged = %d, want %d", st.gasRemaining.UsedRegularGas, want)
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedExecutionGas != want {
t.Fatalf("execution charged = %d, want %d", st.gasRemaining.UsedExecutionGas, want)
}
if want := int64(authBaseState); st.gasRemaining.UsedStateGas != want {
t.Fatalf("state charged = %d, want %d", st.gasRemaining.UsedStateGas, want)
@ -84,8 +84,8 @@ func TestAuthRuntimeChargeWarmAuthority(t *testing.T) {
if err := st.applyAuthorization(rules8037, &auth, map[common.Address]*authTracking{}); err != nil {
t.Fatal(err)
}
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
t.Fatalf("regular charged = %d, want %d (warm authority)", st.gasRemaining.UsedRegularGas, want)
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedExecutionGas != want {
t.Fatalf("execution charged = %d, want %d (warm authority)", st.gasRemaining.UsedExecutionGas, want)
}
if want := int64(authBaseState); st.gasRemaining.UsedStateGas != want {
t.Fatalf("state charged = %d, want %d", st.gasRemaining.UsedStateGas, want)
@ -102,9 +102,9 @@ func TestAuthRuntimeInvalidNoCharge(t *testing.T) {
if err := st.applyAuthorization(rules8037, &bad, map[common.Address]*authTracking{}); err == nil {
t.Fatal("expected invalid-authorization error")
}
if st.gasRemaining.UsedRegularGas != 0 || st.gasRemaining.UsedStateGas != 0 {
if st.gasRemaining.UsedExecutionGas != 0 || st.gasRemaining.UsedStateGas != 0 {
t.Fatalf("charged = <%d,%d>, want <0,0> (invalid authorization)",
st.gasRemaining.UsedRegularGas, st.gasRemaining.UsedStateGas)
st.gasRemaining.UsedExecutionGas, st.gasRemaining.UsedStateGas)
}
}
@ -122,8 +122,8 @@ func TestAuthRuntimeDuplicateAuthorityOnce(t *testing.T) {
if err := st.applyAuthorization(rules8037, &a1, authorities); err != nil {
t.Fatal(err)
}
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
t.Fatalf("regular charged = %d, want %d (once)", st.gasRemaining.UsedRegularGas, want)
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedExecutionGas != want {
t.Fatalf("execution charged = %d, want %d (once)", st.gasRemaining.UsedExecutionGas, want)
}
if want := int64(authWorstState); st.gasRemaining.UsedStateGas != want {
t.Fatalf("state charged = %d, want %d (once)", st.gasRemaining.UsedStateGas, want)

View file

@ -28,9 +28,9 @@ type GasPool struct {
initial uint64
cumulativeUsed uint64
// After 8037 Block gas used is max(cumulativeRegular, cumulativeState).
cumulativeRegular uint64
cumulativeState uint64
// After 8037 Block gas used is max(cumulativeExecution, cumulativeState).
cumulativeExecution uint64
cumulativeState uint64
}
// NewGasPool initializes the gasPool with the given amount.
@ -52,10 +52,10 @@ func (gp *GasPool) CheckGasLegacy(amount uint64) error {
}
// CheckGasAmsterdam performs the EIP-8037 per-tx 2D block-inclusion check:
// the worst-case regular contribution must fit in the regular dimension and
// the worst-case execution contribution must fit in the execution dimension and
// the worst-case state contribution must fit in the state dimension
func (gp *GasPool) CheckGasAmsterdam(regularReservation, stateReservation uint64) error {
if gp.initial-gp.cumulativeRegular < regularReservation {
func (gp *GasPool) CheckGasAmsterdam(executionReservation, stateReservation uint64) error {
if gp.initial-gp.cumulativeExecution < executionReservation {
return ErrGasLimitReached
}
if gp.initial-gp.cumulativeState < stateReservation {
@ -82,20 +82,20 @@ func (gp *GasPool) ChargeGasLegacy(returned uint64, gasUsed uint64) error {
// execution of a message. Previously we subtracted and re-added gas to the
// gaspool. After Amsterdam we only check if we can include the transaction
// and charge the gaspool at the end.
func (gp *GasPool) ChargeGasAmsterdam(txRegular, txState, receiptGasUsed uint64) error {
cumulativeRegular := gp.cumulativeRegular + txRegular
func (gp *GasPool) ChargeGasAmsterdam(txExecution, txState, receiptGasUsed uint64) error {
cumulativeExecution := gp.cumulativeExecution + txExecution
cumulativeState := gp.cumulativeState + txState
blockUsed := max(cumulativeRegular, cumulativeState)
blockUsed := max(cumulativeExecution, cumulativeState)
if gp.initial < blockUsed {
return fmt.Errorf("%w: block gas overflow: initial %d, used %d (regular: %d, state: %d)",
ErrGasLimitReached, gp.initial, blockUsed, cumulativeRegular, cumulativeState)
return fmt.Errorf("%w: block gas overflow: initial %d, used %d (execution: %d, state: %d)",
ErrGasLimitReached, gp.initial, blockUsed, cumulativeExecution, cumulativeState)
}
gp.cumulativeRegular = cumulativeRegular
gp.cumulativeExecution = cumulativeExecution
gp.cumulativeState = cumulativeState
gp.cumulativeUsed += receiptGasUsed
// TODO(rjl, marius), the semantics of this counter is slightly different
// in the context of Amsterdam, the API Gas() should be reworked.
gp.remaining = gp.initial - gp.cumulativeRegular
gp.remaining = gp.initial - gp.cumulativeExecution
return nil
}
@ -109,24 +109,24 @@ func (gp *GasPool) CumulativeUsed() uint64 {
return gp.cumulativeUsed
}
// CumulativeRegular returns the cumulative regular-dimension gas consumed
// CumulativeExecution returns the cumulative execution-dimension gas consumed
// (EIP-8037). It is used to derive the block gas used when transactions are
// charged against independent pools during parallel execution.
func (gp *GasPool) CumulativeRegular() uint64 {
return gp.cumulativeRegular
func (gp *GasPool) CumulativeExecution() uint64 {
return gp.cumulativeExecution
}
// CumulativeState returns the cumulative state-dimension gas consumed
// (EIP-8037). See CumulativeRegular for the rationale.
// (EIP-8037). See CumulativeExecution for the rationale.
func (gp *GasPool) CumulativeState() uint64 {
return gp.cumulativeState
}
// Used returns the amount of consumed gas.
func (gp *GasPool) Used() uint64 {
// After 8037, return max(sum_regular, sum_state)
if gp.cumulativeRegular > 0 || gp.cumulativeState > 0 {
return max(gp.cumulativeRegular, gp.cumulativeState)
// After 8037, return max(sum_execution, sum_state)
if gp.cumulativeExecution > 0 || gp.cumulativeState > 0 {
return max(gp.cumulativeExecution, gp.cumulativeState)
}
// Before 8037, return initial-remaining
if gp.initial < gp.remaining {
@ -138,11 +138,11 @@ func (gp *GasPool) Used() uint64 {
// Snapshot returns the deep-copied object as the snapshot.
func (gp *GasPool) Snapshot() *GasPool {
return &GasPool{
initial: gp.initial,
remaining: gp.remaining,
cumulativeUsed: gp.cumulativeUsed,
cumulativeRegular: gp.cumulativeRegular,
cumulativeState: gp.cumulativeState,
initial: gp.initial,
remaining: gp.remaining,
cumulativeUsed: gp.cumulativeUsed,
cumulativeExecution: gp.cumulativeExecution,
cumulativeState: gp.cumulativeState,
}
}
@ -151,7 +151,7 @@ func (gp *GasPool) Set(other *GasPool) {
gp.initial = other.initial
gp.remaining = other.remaining
gp.cumulativeUsed = other.cumulativeUsed
gp.cumulativeRegular = other.cumulativeRegular
gp.cumulativeExecution = other.cumulativeExecution
gp.cumulativeState = other.cumulativeState
}

View file

@ -75,10 +75,10 @@ type txExecResult struct {
receipt *types.Receipt
accessList *bal.ConstructionBlockAccessList
// regular and state are the EIP-8037 per-transaction
// execution and state are the EIP-8037 per-transaction
// gas contributions to the two block-inclusion dimensions.
regular uint64
state uint64
execution uint64
state uint64
}
// processParallel executes the block's transactions concurrently using the
@ -183,7 +183,7 @@ func (p *StateProcessor) processParallel(ctx context.Context, block *types.Block
if err := gp.CheckGasAmsterdam(min(gasLimit, params.MaxTxGas), gasLimit); err != nil {
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, txs[i].Hash().Hex(), err)
}
if err := gp.ChargeGasAmsterdam(results[i].regular, results[i].state, receipt.GasUsed); err != nil {
if err := gp.ChargeGasAmsterdam(results[i].execution, results[i].state, receipt.GasUsed); err != nil {
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, txs[i].Hash().Hex(), err)
}
// Correct the receipt object with block-level fields
@ -305,7 +305,7 @@ func (p *StateProcessor) executeTransactionsParallel(block *types.Block, parentR
results[i] = txExecResult{
receipt: receipt,
accessList: accessList,
regular: gp.CumulativeRegular(),
execution: gp.CumulativeExecution(),
state: gp.CumulativeState(),
}
}

View file

@ -493,23 +493,23 @@ func (st *stateTransition) buyGas() error {
// gas remaining after the intrinsic cost has been deducted.
//
// After Amsterdam (EIP-8037) the intrinsic cost counts towards the EIP-7825
// regular-gas cap:
// execution-gas cap:
//
// execution_gas = tx.gas - intrinsic_gas
// regular_gas_budget = TX_MAX_GAS_LIMIT - intrinsic_gas
// gas_left = min(regular_gas_budget, execution_gas)
// state_gas_reservoir = execution_gas - gas_left
// evm_gas = tx.gas - intrinsic_gas
// execution_gas_budget = TX_MAX_GAS_LIMIT - intrinsic_gas
// gas_left = min(execution_gas_budget, evm_gas)
// state_gas_reservoir = evm_gas - gas_left
func (st *stateTransition) initRuntimeGasBudget(rules params.Rules, intrinsicGas uint64) {
executionGas := st.msg.GasLimit - intrinsicGas
gasLeft := executionGas
evmGas := st.msg.GasLimit - intrinsicGas
gasLeft := evmGas
if rules.IsAmsterdam {
gasLeft = min(params.MaxTxGas-intrinsicGas, executionGas)
gasLeft = min(params.MaxTxGas-intrinsicGas, evmGas)
}
st.gasRemaining = vm.NewGasBudget(gasLeft, executionGas-gasLeft)
st.gasRemaining = vm.NewGasBudget(gasLeft, evmGas-gasLeft)
if st.evm.Config.Tracer.HasGasHook() {
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{}, tracing.Gas{Regular: st.msg.GasLimit}, tracing.GasChangeTxInitialBalance)
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Regular: st.msg.GasLimit}, st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{}, tracing.Gas{Execution: st.msg.GasLimit}, tracing.GasChangeTxInitialBalance)
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Execution: st.msg.GasLimit}, st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
}
}
@ -810,13 +810,13 @@ func (st *stateTransition) executeCreate(rules params.Rules, value *uint256.Int)
if rules.IsAmsterdam && chargedCreation && vmerr != nil {
st.gasRemaining.RefundState(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
}
// If the top-most frame halted, drain the leftover regular gas rather
// If the top-most frame halted, drain the leftover execution gas rather
// than returning it to the sender. The frame exit itself already burned
// its gas left, but the refill above repays the regular gas the charge
// its gas left, but the refill above repays the execution gas the charge
// originally borrowed, and on a halt that repayment must be burned as
// well. The state dimension is left untouched.
if rules.IsAmsterdam && vmerr != nil && vmerr != vm.ErrExecutionReverted {
st.gasRemaining.DrainRegular()
st.gasRemaining.DrainExecution()
}
return ret, vmerr
}
@ -865,13 +865,13 @@ func (st *stateTransition) executeCall(rules params.Rules, value *uint256.Int) (
if rules.IsAmsterdam && vmerr != nil && !value.IsZero() && st.evm.StateDB.Empty(st.to()) {
st.gasRemaining.RefundState(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
}
// If the top-most frame halted, drain the leftover regular gas rather
// If the top-most frame halted, drain the leftover execution gas rather
// than returning it to the sender. The frame exit itself already burned
// its gas left, but the refill above repays the regular gas the charge
// its gas left, but the refill above repays the execution gas the charge
// originally borrowed, and on a halt that repayment must be burned as
// well.
if rules.IsAmsterdam && vmerr != nil && vmerr != vm.ErrExecutionReverted {
st.gasRemaining.DrainRegular()
st.gasRemaining.DrainExecution()
}
return ret, vmerr
}
@ -885,14 +885,14 @@ func (st *stateTransition) traceHaltedTopFrame(typ vm.OpCode, to common.Address,
return
}
if tracer.OnEnter != nil {
tracer.OnEnter(0, byte(typ), st.msg.From, to, input, entryGas.RegularGas, value.ToBig())
tracer.OnEnter(0, byte(typ), st.msg.From, to, input, entryGas.ExecutionGas, value.ToBig())
}
if tracer.HasGasHook() {
tracer.EmitGasChange(tracing.Gas{}, entryGas.AsTracing(), tracing.GasChangeCallInitialBalance)
tracer.EmitGasChange(entryGas.AsTracing(), endGas.AsTracing(), tracing.GasChangeCallFailedExecution)
}
if tracer.OnExit != nil {
tracer.OnExit(0, nil, entryGas.RegularGas, vm.VMErrorFromErr(vm.ErrOutOfGas), true)
tracer.OnExit(0, nil, entryGas.ExecutionGas, vm.VMErrorFromErr(vm.ErrOutOfGas), true)
}
}
@ -937,9 +937,9 @@ func (st *stateTransition) chargeCallRecipientEIP2780(value *uint256.Int) bool {
if target, delegated := types.ParseDelegation(st.state.GetCode(to)); delegated {
// Pay the delegation-target access before the target is warmed and
// its code resolved (loaded).
cost := vm.GasCosts{RegularGas: params.ColdAccountAccessAmsterdam}
cost := vm.GasCosts{ExecutionGas: params.ColdAccountAccessAmsterdam}
if st.state.AddressInAccessList(target) {
cost.RegularGas = params.WarmAccountAccessAmsterdam
cost.ExecutionGas = params.WarmAccountAccessAmsterdam
}
if !st.chargeRuntimeGas(cost) {
return false
@ -954,7 +954,7 @@ func (st *stateTransition) chargeCallRecipientEIP2780(value *uint256.Int) bool {
// settleGas finalizes the per-tx gas accounting after EVM execution:
//
// - Snapshots the EIP-8037 block-level 2D figures (tx_regular_gas,
// - Snapshots the EIP-8037 block-level 2D figures (tx_execution_gas,
// tx_state_gas) before any refund.
// - Computes the receipt scalar tx_gas_used by applying the EIP-3529
// refund and the EIP-7623 calldata floor.
@ -969,19 +969,19 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
// EIP-8037:
// tx_gas_used_before_refund = tx.gas - tx_output.gas_left - tx_output.state_gas_reservoir
// tx_state_gas = tx_output.execution_state_gas_used
// tx_regular_gas = max(tx_gas_used_before_refund - tx_state_gas, calldata_floor_gas_cost)
gasLeft := st.gasRemaining.RegularGas + st.gasRemaining.StateGas
// tx_execution_gas = max(tx_gas_used_before_refund - tx_state_gas, calldata_floor_gas_cost)
gasLeft := st.gasRemaining.ExecutionGas + st.gasRemaining.StateGas
gasUsedBeforeRefund := st.msg.GasLimit - gasLeft
if gasUsedBeforeRefund < txStateGas {
return 0, 0, fmt.Errorf("negative topmost frame regular gas usage, total: %d, state: %d", gasUsedBeforeRefund, txStateGas)
return 0, 0, fmt.Errorf("negative topmost frame execution gas usage, total: %d, state: %d", gasUsedBeforeRefund, txStateGas)
}
txRegularGas := max(gasUsedBeforeRefund-txStateGas, floorDataGas)
txExecutionGas := max(gasUsedBeforeRefund-txStateGas, floorDataGas)
// EIP-3529: tx_gas_refund = min(tx_gas_used_before_refund/5, refund_counter).
refund := st.calcRefund(gasUsedBeforeRefund)
if st.evm.Config.Tracer.HasGasHook() {
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Regular: gasLeft}, tracing.Gas{Regular: gasLeft + refund}, tracing.GasChangeTxRefunds)
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Execution: gasLeft}, tracing.Gas{Execution: gasLeft + refund}, tracing.GasChangeTxRefunds)
}
gasLeft += refund
gasUsed = gasUsedBeforeRefund - refund
@ -991,7 +991,7 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
if rules.IsPrague && gasUsed < floorDataGas {
diff := floorDataGas - gasUsed
if st.evm.Config.Tracer.HasGasHook() {
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Regular: gasLeft}, tracing.Gas{Regular: gasLeft - diff}, tracing.GasChangeTxDataFloor)
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Execution: gasLeft}, tracing.Gas{Execution: gasLeft - diff}, tracing.GasChangeTxDataFloor)
}
gasLeft -= diff
gasUsed = floorDataGas
@ -1000,7 +1000,7 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
// Settle down the final gas consumption in the block-level pool
if rules.IsAmsterdam {
if err = st.gp.ChargeGasAmsterdam(txRegularGas, txStateGas, gasUsed); err != nil {
if err = st.gp.ChargeGasAmsterdam(txExecutionGas, txStateGas, gasUsed); err != nil {
return 0, 0, err
}
} else {
@ -1015,7 +1015,7 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
st.state.AddBalance(st.msg.From, refund, tracing.BalanceIncreaseGasReturn)
if st.evm.Config.Tracer.HasGasHook() {
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Regular: gasLeft}, tracing.Gas{}, tracing.GasChangeTxLeftOverReturned)
st.evm.Config.Tracer.EmitGasChange(tracing.Gas{Execution: gasLeft}, tracing.Gas{}, tracing.GasChangeTxLeftOverReturned)
}
}
return gasUsed, peakUsed, nil
@ -1100,7 +1100,7 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
// write to tx.to here is still the first paid write.
hasValue := st.msg.Value != nil && !st.msg.Value.IsZero()
if !track.written && authority != st.msg.From && (authority != st.to() || !hasValue) {
cost.RegularGas += params.AccountWriteAmsterdam
cost.ExecutionGas += params.AccountWriteAmsterdam
track.written = true
}
// Durable state growth of the new account

View file

@ -264,7 +264,7 @@ func TestIntrinsicGas(t *testing.T) {
isHomestead: true,
isEIP2028: true,
isAmsterdam: true,
// EIP-2780: creation regular gas is TxBaseCost + CreateAccess (23,000);
// EIP-2780: creation execution gas is TxBaseCost + CreateAccess (23,000);
// the new-account state charge is applied at runtime.
want: params.TxBaseCost2780 + params.CreateAccessAmsterdam,
},

View file

@ -164,7 +164,7 @@ type (
// FaultHook is invoked when an error occurs during the execution of an opcode.
FaultHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, depth int, err error)
// GasChangeHook reports changes to the regular execution gas. Tracers
// GasChangeHook reports changes to the execution gas. Tracers
// that don't need the EIP-8037 (Amsterdam) state-access dimension can
// implement only this hook; it fires unchanged across the fork. If both
// this and GasChangeHookV2 are set, only V2 is invoked; implement exactly
@ -173,7 +173,7 @@ type (
// GasChangeHookV2 is the multi-dimensional successor to GasChangeHook,
// invoked when any gas dimension changes and exposing the EIP-8037
// (Amsterdam) state-access dimension alongside the regular one. The
// (Amsterdam) state-access dimension alongside the execution one. The
// non-changing dimension is passed through unchanged in both `old` and
// `new`, so consumers always see the complete gas vector. Pre-Amsterdam
// the State field is always zero, making a V2-only tracer behave exactly
@ -303,7 +303,7 @@ func (h *Hooks) HasGasHook() bool {
// EmitGasChange dispatches a gas change event to the registered hooks. If the
// multi-dimensional OnGasChangeV2 hook is set it is invoked with the full Gas
// vectors; otherwise the single-dimensional OnGasChange hook is invoked with
// the regular-gas dimension only. The call is a no-op when the receiver is
// the execution-gas dimension only. The call is a no-op when the receiver is
// nil, when neither hook is registered, or when the reason is GasChangeIgnored.
//
// Call sites SHOULD use this helper instead of invoking the hooks directly so
@ -317,7 +317,7 @@ func (h *Hooks) EmitGasChange(old, new Gas, reason GasChangeReason) {
return
}
if h.OnGasChange != nil {
h.OnGasChange(old.Regular, new.Regular, reason)
h.OnGasChange(old.Execution, new.Execution, reason)
}
}
@ -390,16 +390,16 @@ const (
)
// Gas represents a multi-dimensional gas budget introduced by EIP-8037.
// It carries the regular execution gas and the state-access gas, which are
// It carries the execution gas and the state-access gas, which are
// metered independently from the Amsterdam fork onwards.
//
// Before Amsterdam, gas metering is single-dimensional and only the Regular
// Before Amsterdam, gas metering is single-dimensional and only the Execution
// field is meaningful; State is always zero. The struct is shaped so that
// pre-Amsterdam call sites can populate it as Gas{Regular: g} without loss
// pre-Amsterdam call sites can populate it as Gas{Execution: g} without loss
// of fidelity relative to the legacy single-uint64 hook.
type Gas struct {
Regular uint64 // Regular is the budget for ordinary execution gas.
State uint64 // State is the budget dedicated to state-access gas (zero pre-Amsterdam).
Execution uint64 // Execution is the budget for ordinary execution gas.
State uint64 // State is the budget dedicated to state-access gas (zero pre-Amsterdam).
}
// GasChangeReason is used to indicate the reason for a gas change, useful

View file

@ -126,10 +126,10 @@ func (c *Contract) Caller() common.Address {
return c.caller
}
// chargeRegular deducts regular gas only, with tracer integration.
// Returns false on OOG. Delegates the arithmetic to GasBudget.ChargeRegular.
func (c *Contract) chargeRegular(r uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) bool {
prior, ok := c.Gas.ChargeRegular(r)
// chargeExecution deducts execution gas only, with tracer integration.
// Returns false on OOG. Delegates the arithmetic to GasBudget.ChargeExecution.
func (c *Contract) chargeExecution(r uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) bool {
prior, ok := c.Gas.ChargeExecution(r)
if !ok {
return false
}
@ -139,7 +139,7 @@ func (c *Contract) chargeRegular(r uint64, logger *tracing.Hooks, reason tracing
return true
}
// chargeState deducts state gas (spilling into regular when the reservoir is
// chargeState deducts state gas (spilling into execution when the reservoir is
// exhausted), with tracer integration. Returns false on OOG.
func (c *Contract) chargeState(s uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) bool {
prior, ok := c.Gas.ChargeState(s)
@ -171,18 +171,18 @@ func (c *Contract) refundGas(child GasBudget, logger *tracing.Hooks, reason trac
}
}
// forwardGas drains `regular` regular gas and the entire state reservoir
// forwardGas drains `execution` gas and the entire state reservoir
// from this contract's running budget and returns the initial GasBudget for
// a child frame. The caller's UsedRegularGas is bumped by the forwarded
// a child frame. The caller's UsedExecutionGas is bumped by the forwarded
// amount so that the absorb-on-return path correctly reclaims the unused
// portion. Thin wrapper around GasBudget.Forward with tracer integration.
//
// Caller must ensure `regular` is no larger than the running balance (the
// Caller must ensure `execution` is no larger than the running balance (the
// opcode's dynamic gas table is expected to validate that before invoking
// the opcode handler).
func (c *Contract) forwardGas(regular uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) GasBudget {
func (c *Contract) forwardGas(execution uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) GasBudget {
prior := c.Gas
child := c.Gas.Forward(regular)
child := c.Gas.Forward(execution)
if logger.HasGasHook() && reason != tracing.GasChangeIgnored {
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
}

View file

@ -271,7 +271,7 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
// - any error that occurred
func RunPrecompiledContract(stateDB StateDB, p PrecompiledContract, address common.Address, input []byte, gas GasBudget, logger *tracing.Hooks, rules params.Rules, cache *PrecompileCache) (ret []byte, remaining GasBudget, err error) {
gasCost := p.RequiredGas(input)
prior, ok := gas.ChargeRegular(gasCost)
prior, ok := gas.ChargeExecution(gasCost)
if !ok {
return nil, gas, ErrOutOfGas
}

View file

@ -86,22 +86,22 @@ func run8037(t *testing.T, code []byte, gas GasBudget, value *uint256.Int, setup
// assertBudgetSane verifies the GasBudget conservation identities that must hold
// for any frame exit (success, revert or halt), validating the whole vector.
//
// regular: RegularGas + UsedRegularGas + Spilled == initial.RegularGas
// state: StateGas + UsedStateGas == initial.StateGas + Spilled
// scalar: Used(initial) == UsedRegularGas + UsedStateGas
// execution: ExecutionGas + UsedExecutionGas + Spilled == initial.ExecutionGas
// state: StateGas + UsedStateGas == initial.StateGas + Spilled
// scalar: Used(initial) == UsedExecutionGas + UsedStateGas
func assertBudgetSane(t *testing.T, initial, got GasBudget) {
t.Helper()
if got.RegularGas+got.UsedRegularGas+got.Spilled != initial.RegularGas {
t.Fatalf("regular not conserved: R=%d usedR=%d spilled=%d, want sum %d",
got.RegularGas, got.UsedRegularGas, got.Spilled, initial.RegularGas)
if got.ExecutionGas+got.UsedExecutionGas+got.Spilled != initial.ExecutionGas {
t.Fatalf("execution not conserved: R=%d usedR=%d spilled=%d, want sum %d",
got.ExecutionGas, got.UsedExecutionGas, got.Spilled, initial.ExecutionGas)
}
if int64(got.StateGas)+got.UsedStateGas != int64(initial.StateGas)+int64(got.Spilled) {
t.Fatalf("state not conserved: S=%d usedS=%d spilled=%d, want %d+spilled",
got.StateGas, got.UsedStateGas, got.Spilled, initial.StateGas)
}
if int64(got.Used(initial)) != int64(got.UsedRegularGas)+got.UsedStateGas {
if int64(got.Used(initial)) != int64(got.UsedExecutionGas)+got.UsedStateGas {
t.Fatalf("scalar mismatch: used=%d, usedR=%d usedS=%d",
got.Used(initial), got.UsedRegularGas, got.UsedStateGas)
got.Used(initial), got.UsedExecutionGas, got.UsedStateGas)
}
}
@ -178,7 +178,7 @@ func TestSStoreOtherWrite(t *testing.T) {
}
// New-slot charge is metered at the opcode: with a reservoir smaller than the
// charge it spills into regular gas exactly at the SSTORE.
// charge it spills into execution gas exactly at the SSTORE.
func TestSStoreChargedAtOpcodeEnd(t *testing.T) {
_, res, err := run8037(t, sstore(0, 1), NewGasBudget(1_000_000, 100), new(uint256.Int), nil)
if err != nil {
@ -194,20 +194,20 @@ func TestSStoreChargedAtOpcodeEnd(t *testing.T) {
// sentry (2300) for a 2306 budget. Under EIP-8038 the cold-slot access that
// follows a cleared sentry costs COLD_STORAGE_ACCESS (3000).
func TestSStoreStipendExcludesReservoir(t *testing.T) {
// regular at the sentry, huge reservoir: must still fail, proving the
// execution at the sentry, huge reservoir: must still fail, proving the
// reservoir does not count toward the sentry.
if _, _, err := run8037(t, sstore(0, 1), NewGasBudget(2306, math.MaxUint64/2), new(uint256.Int), setSlot(0, 1)); err == nil {
t.Fatal("expected sentry failure with regular gas at the limit")
t.Fatal("expected sentry failure with execution gas at the limit")
}
// Enough regular gas to clear the sentry and pay the cold-slot access
// Enough execution gas to clear the sentry and pay the cold-slot access
// (6 for the PUSH1s + COLD_STORAGE_ACCESS) succeeds with a huge reservoir.
regular := 6 + params.ColdStorageAccessAmsterdam
if _, _, err := run8037(t, sstore(0, 1), NewGasBudget(regular, math.MaxUint64/2), new(uint256.Int), setSlot(0, 1)); err != nil {
execution := 6 + params.ColdStorageAccessAmsterdam
if _, _, err := run8037(t, sstore(0, 1), NewGasBudget(execution, math.MaxUint64/2), new(uint256.Int), setSlot(0, 1)); err != nil {
t.Fatalf("unexpected failure above sentry: %v", err)
}
// One gas short of the cold-slot access still fails (now on OOG, not sentry).
if _, _, err := run8037(t, sstore(0, 1), NewGasBudget(regular-1, math.MaxUint64/2), new(uint256.Int), setSlot(0, 1)); err == nil {
t.Fatal("expected OOG when regular gas cannot cover cold-slot access")
if _, _, err := run8037(t, sstore(0, 1), NewGasBudget(execution-1, math.MaxUint64/2), new(uint256.Int), setSlot(0, 1)); err == nil {
t.Fatal("expected OOG when execution gas cannot cover cold-slot access")
}
}
@ -521,7 +521,7 @@ func TestSelfdestructPreexistingNoRefill(t *testing.T) {
// ===================== Reservoir / gas_left mechanics =====================
// State-gas is drawn from the reservoir first: a charge within reservoir size
// does not spill into regular gas.
// does not spill into execution gas.
func TestReservoirDrawnFirst(t *testing.T) {
_, res, err := run8037(t, sstore(0, 1), NewGasBudget(1_000_000, 200_000), new(uint256.Int), nil)
if err != nil {
@ -547,7 +547,7 @@ func TestGasOpcodeExcludesReservoir(t *testing.T) {
}
}
// Refills are LIFO: borrowed regular gas is repaid before the reservoir. With a
// Refills are LIFO: borrowed execution gas is repaid before the reservoir. With a
// zero reservoir, a 0->x->0 SSTORE repays the spill and leaves the reservoir at 0.
func TestLIFORefillOrder(t *testing.T) {
code := append(sstore(0, 1), sstore(0, 0)...)
@ -589,30 +589,30 @@ func TestStateGasMeteredAtFrameBoundary(t *testing.T) {
// ===================== LIFO refill vector invariant =========================
// Charge A then B (both spilling into regular because the reservoir is too
// small), then refill only A. The refill must repay the borrowed regular gas
// Charge A then B (both spilling into execution because the reservoir is too
// small), then refill only A. The refill must repay the borrowed execution gas
// first (Spilled -> 0) before crediting the reservoir, leaving B outstanding.
func TestLIFORefillRepaysRegularBeforeReservoir(t *testing.T) {
func TestLIFORefillRepaysExecutionBeforeReservoir(t *testing.T) {
initial := NewGasBudget(1000, 100) // reservoir covers only 100 of state gas
b := initial
b.ChargeState(150) // A: 100 from reservoir, 50 spills into regular
b.ChargeState(150) // A: 100 from reservoir, 50 spills into execution
b.ChargeState(30) // B: reservoir empty, all 30 spills
if b.Spilled != 80 || b.StateGas != 0 {
t.Fatalf("after A+B: spilled=%d reservoir=%d, want 80/0", b.Spilled, b.StateGas)
}
b.RefundState(150) // refill A: repay 80 to regular first, 70 tops reservoir
b.RefundState(150) // refill A: repay 80 to execution first, 70 tops reservoir
if b.Spilled != 0 {
t.Fatalf("spilled=%d, want 0 (regular repaid before reservoir)", b.Spilled)
t.Fatalf("spilled=%d, want 0 (execution repaid before reservoir)", b.Spilled)
}
if b.StateGas != 70 {
t.Fatalf("reservoir=%d, want 70 (remainder after repaying regular)", b.StateGas)
t.Fatalf("reservoir=%d, want 70 (remainder after repaying execution)", b.StateGas)
}
assertBudgetSane(t, initial, b)
}
// Fuzz arbitrary sequences of state/regular charges and LIFO refills around the
// Fuzz arbitrary sequences of state/execution charges and LIFO refills around the
// reservoir/spill boundary: the GasBudget vector must stay self-consistent after
// every op and across all three frame-exit forms, and refilling every charge
// must restore the state side exactly (reservoir to initial, nothing borrowed).
@ -624,14 +624,14 @@ func TestLIFOVectorInvariantUnderRandomOps(t *testing.T) {
outstanding := int64(0) // state-gas charged but not yet refilled
for step := 0; step < 40; step++ {
switch rng.Intn(3) {
case 0: // state charge (may spill into regular)
case 0: // state charge (may spill into execution)
if s := uint64(rng.Intn(400)); b.CanAfford(GasCosts{StateGas: s}) {
b.ChargeState(s)
outstanding += int64(s)
}
case 1: // regular charge
if r := uint64(rng.Intn(400)); b.CanAfford(GasCosts{RegularGas: r}) {
b.ChargeRegular(r)
case 1: // execution charge
if r := uint64(rng.Intn(400)); b.CanAfford(GasCosts{ExecutionGas: r}) {
b.ChargeExecution(r)
}
case 2: // LIFO refill of part of the outstanding state gas
if outstanding > 0 {
@ -666,12 +666,12 @@ func concat(parts ...[]byte) []byte {
}
// assertHalted checks the predictable terminal budget of an exceptionally
// halted frame: regular gas fully consumed, state restored to the frame's
// halted frame: execution gas fully consumed, state restored to the frame's
// initial reservoir, and no net state-gas used.
func assertHalted(t *testing.T, initial, got GasBudget) {
t.Helper()
if got.RegularGas != 0 {
t.Fatalf("RegularGas = %d, want 0 (gas_left consumed on halt)", got.RegularGas)
if got.ExecutionGas != 0 {
t.Fatalf("ExecutionGas = %d, want 0 (gas_left consumed on halt)", got.ExecutionGas)
}
if got.StateGas != initial.StateGas {
t.Fatalf("StateGas = %d, want %d (reservoir restored)", got.StateGas, initial.StateGas)

View file

@ -15,7 +15,7 @@
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Opcode-level tests for EIP-8038 (state-access gas cost update). They reuse the
// Amsterdam harness from eip8037_test.go and assert the re-priced regular-gas,
// Amsterdam harness from eip8037_test.go and assert the re-priced execution-gas,
// state-gas and refund-counter accounting.
package vm
@ -49,7 +49,7 @@ func run8038(t *testing.T, code []byte, gas GasBudget, value *uint256.Int, setup
}
// TestEIP8038SStore exercises SSTORE under Amsterdam (EIP-8037 + EIP-8038),
// asserting the two-dimensional charge (regular + state gas) and the net refund
// asserting the two-dimensional charge (execution + state gas) and the net refund
// counter. It covers single stores in isolation (the EIP-8038 cases-table rows,
// cold access), the warm-access variants, the dirty-slot refund reversals and
// multi-store round trips.
@ -70,7 +70,7 @@ func TestEIP8038SStore(t *testing.T) {
)
set := uint64(params.StorageCreationSize * params.CostPerStateByte) // GAS_STORAGE_SET
// access(n) is the access-only regular cost for n stores: cold first, warm rest.
// access(n) is the access-only execution cost for n stores: cold first, warm rest.
access := func(n uint64) uint64 { return cold + (n-1)*warm }
cases := []struct {
@ -114,8 +114,8 @@ func TestEIP8038SStore(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if res.UsedRegularGas != tc.wantReg {
t.Errorf("regular gas = %d, want %d", res.UsedRegularGas, tc.wantReg)
if res.UsedExecutionGas != tc.wantReg {
t.Errorf("execution gas = %d, want %d", res.UsedExecutionGas, tc.wantReg)
}
if res.UsedStateGas != tc.wantState {
t.Errorf("state gas = %d, want %d", res.UsedStateGas, tc.wantState)
@ -136,8 +136,8 @@ func TestEIP8038SLoad(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if want := push + params.ColdStorageAccessAmsterdam; res.UsedRegularGas != want {
t.Fatalf("cold SLOAD = %d, want %d", res.UsedRegularGas, want)
if want := push + params.ColdStorageAccessAmsterdam; res.UsedExecutionGas != want {
t.Fatalf("cold SLOAD = %d, want %d", res.UsedExecutionGas, want)
}
// PUSH1 0x00; SLOAD; PUSH1 0x00; SLOAD -> second access is warm.
warm := []byte{0x60, 0x00, 0x54, 0x60, 0x00, 0x54}
@ -146,8 +146,8 @@ func TestEIP8038SLoad(t *testing.T) {
t.Fatal(err)
}
want := 2*push + params.ColdStorageAccessAmsterdam + params.WarmStorageReadCostEIP2929
if res.UsedRegularGas != want {
t.Fatalf("cold+warm SLOAD = %d, want %d", res.UsedRegularGas, want)
if res.UsedExecutionGas != want {
t.Fatalf("cold+warm SLOAD = %d, want %d", res.UsedExecutionGas, want)
}
}
@ -170,8 +170,8 @@ func TestEIP8038AccountAccess(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if want := push20 + cold; res.UsedRegularGas != want {
t.Fatalf("cold BALANCE = %d, want %d", res.UsedRegularGas, want)
if want := push20 + cold; res.UsedExecutionGas != want {
t.Fatalf("cold BALANCE = %d, want %d", res.UsedExecutionGas, want)
}
})
t.Run("EXTCODEHASH", func(t *testing.T) {
@ -180,8 +180,8 @@ func TestEIP8038AccountAccess(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if want := push20 + cold; res.UsedRegularGas != want {
t.Fatalf("cold EXTCODEHASH = %d, want %d", res.UsedRegularGas, want)
if want := push20 + cold; res.UsedExecutionGas != want {
t.Fatalf("cold EXTCODEHASH = %d, want %d", res.UsedExecutionGas, want)
}
})
t.Run("EXTCODESIZE adds WARM_ACCESS", func(t *testing.T) {
@ -190,8 +190,8 @@ func TestEIP8038AccountAccess(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if want := push20 + cold + warm; res.UsedRegularGas != want {
t.Fatalf("cold EXTCODESIZE = %d, want %d", res.UsedRegularGas, want)
if want := push20 + cold + warm; res.UsedExecutionGas != want {
t.Fatalf("cold EXTCODESIZE = %d, want %d", res.UsedExecutionGas, want)
}
})
t.Run("EXTCODECOPY adds WARM_ACCESS", func(t *testing.T) {
@ -204,14 +204,14 @@ func TestEIP8038AccountAccess(t *testing.T) {
t.Fatal(err)
}
// three PUSH1 + one PUSH20 = 12 gas, zero-length copy => no memory/copy gas.
if want := uint64(12) + cold + warm; res.UsedRegularGas != want {
t.Fatalf("cold EXTCODECOPY = %d, want %d", res.UsedRegularGas, want)
if want := uint64(12) + cold + warm; res.UsedExecutionGas != want {
t.Fatalf("cold EXTCODECOPY = %d, want %d", res.UsedExecutionGas, want)
}
})
}
// callFamily8038 builds a zero-input/output call-family operation that forwards
// all remaining regular gas and discards its success flag. CALL and CALLCODE
// all remaining execution gas and discards its success flag. CALL and CALLCODE
// take a value argument; DELEGATECALL and STATICCALL do not.
func callFamily8038(to common.Address, op OpCode, value byte) []byte {
code := []byte{0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00}
@ -248,7 +248,7 @@ func TestEIP8038Calls(t *testing.T) {
}{
{"call/cold", CALL, 0, false, callBase + cold, 0},
// A callee that immediately returns gives the 2,300 stipend back, so
// the net regular cost is ACCOUNT_WRITE.
// the net execution cost is ACCOUNT_WRITE.
{"call/value", CALL, 1, true, callBase + cold + params.AccountWriteAmsterdam, stateGasNewAccount},
{"callcode/value", CALLCODE, 1, true, callBase + cold + params.AccountWriteAmsterdam, 0},
{"delegatecall/cold", DELEGATECALL, 0, false, plainBase + cold, 0},
@ -264,8 +264,8 @@ func TestEIP8038Calls(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if res.UsedRegularGas != tc.wantReg {
t.Fatalf("regular gas = %d, want %d", res.UsedRegularGas, tc.wantReg)
if res.UsedExecutionGas != tc.wantReg {
t.Fatalf("execution gas = %d, want %d", res.UsedExecutionGas, tc.wantReg)
}
if res.UsedStateGas != tc.wantState {
t.Fatalf("state gas = %d, want %d", res.UsedStateGas, tc.wantState)
@ -281,8 +281,8 @@ func TestEIP8038Calls(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if want := 2*callBase + cold; res.UsedRegularGas != want {
t.Fatalf("cold+warm CALL = %d, want %d", res.UsedRegularGas, want)
if want := 2*callBase + cold; res.UsedExecutionGas != want {
t.Fatalf("cold+warm CALL = %d, want %d", res.UsedExecutionGas, want)
}
// Calling an EIP-7702 authority accesses both the authority and its
@ -300,12 +300,12 @@ func TestEIP8038Calls(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if want := callBase + cold + params.ColdAccountAccessAmsterdam; res.UsedRegularGas != want {
t.Fatalf("delegated CALL = %d, want %d (authority + target)", res.UsedRegularGas, want)
if want := callBase + cold + params.ColdAccountAccessAmsterdam; res.UsedExecutionGas != want {
t.Fatalf("delegated CALL = %d, want %d (authority + target)", res.UsedExecutionGas, want)
}
// A value CALL receives the 2,300 stipend even when it asks to forward no
// regular gas. If the child burns that stipend, the full CALL_VALUE
// execution gas. If the child burns that stipend, the full CALL_VALUE
// (ACCOUNT_WRITE + stipend) remains charged to the caller.
stipendTarget := common.BytesToAddress([]byte("stipend-target"))
base := callFamily8038(stipendTarget, CALL, 1)
@ -319,13 +319,13 @@ func TestEIP8038Calls(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if want := 5*push1 + push20 + push1 + pop + params.WarmAccountAccessAmsterdam + cold + params.CallValueTransferAmsterdam; res.UsedRegularGas != want {
t.Fatalf("value CALL with burnt stipend = %d, want %d", res.UsedRegularGas, want)
if want := 5*push1 + push20 + push1 + pop + params.WarmAccountAccessAmsterdam + cold + params.CallValueTransferAmsterdam; res.UsedExecutionGas != want {
t.Fatalf("value CALL with burnt stipend = %d, want %d", res.UsedExecutionGas, want)
}
}
// TestEIP8038Create checks that CREATE and CREATE2 always pay CREATE_ACCESS
// in regular gas. With otherwise identical initcode, CREATE2 additionally has
// in execution gas. With otherwise identical initcode, CREATE2 additionally has
// one salt push and the address-hash word charge.
func TestEIP8038Create(t *testing.T) {
create, _, err := run8038(t, deployCode(deploy0Init, false, 0), hugeBudget(), new(uint256.Int), nil)
@ -341,17 +341,17 @@ func TestEIP8038Create(t *testing.T) {
const outer = uint64(3 + 3 + 3 + 3 + 3*3)
const init = uint64(2 * 3)
want := outer + params.CreateAccessAmsterdam + params.InitCodeWordGas + init
if create.UsedRegularGas != want {
t.Fatalf("CREATE regular gas = %d, want %d", create.UsedRegularGas, want)
if create.UsedExecutionGas != want {
t.Fatalf("CREATE execution gas = %d, want %d", create.UsedExecutionGas, want)
}
if want := create.UsedRegularGas + 3 + params.Keccak256WordGas; create2.UsedRegularGas != want {
t.Fatalf("CREATE2 regular gas = %d, want %d", create2.UsedRegularGas, want)
if want := create.UsedExecutionGas + 3 + params.Keccak256WordGas; create2.UsedExecutionGas != want {
t.Fatalf("CREATE2 execution gas = %d, want %d", create2.UsedExecutionGas, want)
}
}
// TestEIP8038SelfdestructAccountWrite checks that SELFDESTRUCT sending a positive
// balance to an empty account is charged the cold access, an additional
// ACCOUNT_WRITE (regular) and GAS_NEW_ACCOUNT (state).
// ACCOUNT_WRITE (execution) and GAS_NEW_ACCOUNT (state).
func TestEIP8038SelfdestructAccountWrite(t *testing.T) {
beneficiary := common.BytesToAddress([]byte("fresh-beneficiary"))
// PUSH20 beneficiary; SELFDESTRUCT
@ -368,8 +368,8 @@ func TestEIP8038SelfdestructAccountWrite(t *testing.T) {
}
const push20 = uint64(3)
wantReg := push20 + params.SelfdestructGasEIP150 + params.ColdAccountAccessAmsterdam + params.AccountWriteAmsterdam
if res.UsedRegularGas != wantReg {
t.Fatalf("regular gas = %d, want %d", res.UsedRegularGas, wantReg)
if res.UsedExecutionGas != wantReg {
t.Fatalf("execution gas = %d, want %d", res.UsedExecutionGas, wantReg)
}
if want := int64(params.AccountCreationSize * params.CostPerStateByte); res.UsedStateGas != want {
t.Fatalf("state gas = %d, want %d", res.UsedStateGas, want)

View file

@ -370,8 +370,8 @@ func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, er
addr := common.Address(a.Bytes20())
code := evm.StateDB.GetCode(addr)
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas.RegularGas)
scope.Contract.chargeRegular(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas.ExecutionGas)
scope.Contract.chargeExecution(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
if consumed < wanted {
return nil, ErrOutOfGas
}
@ -396,8 +396,8 @@ func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
// advanced past this boundary.
contractAddr := scope.Contract.Address()
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas)
scope.Contract.chargeRegular(wanted, evm.Config.Tracer, tracing.GasChangeUnspecified)
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.ExecutionGas)
scope.Contract.chargeExecution(wanted, evm.Config.Tracer, tracing.GasChangeUnspecified)
if consumed < wanted {
return nil, ErrOutOfGas
}
@ -423,8 +423,8 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
contractAddr := scope.Contract.Address()
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.RegularGas)
scope.Contract.chargeRegular(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas.ExecutionGas)
scope.Contract.chargeExecution(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
if consumed < wanted {
return nil, ErrOutOfGas
}

View file

@ -293,8 +293,8 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
// list in write mode. If there is enough gas paying for the addition of the code
// hash leaf to the access list, then account creation will proceed unimpaired.
// Thus, only pay for the creation of the code hash leaf here.
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
if _, ok := gas.ChargeRegular(wgas); !ok {
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.ExecutionGas, false)
if _, ok := gas.ChargeExecution(wgas); !ok {
evm.StateDB.RevertToSnapshot(snapshot)
return nil, gas.ExitHalt(), ErrOutOfGas
}
@ -556,8 +556,8 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
// Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 {
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas})
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.ExecutionGas)
prior, ok := gas.Charge(GasCosts{ExecutionGas: statelessGas})
if !ok {
return nil, common.Address{}, gas.ExitHalt(), ErrOutOfGas
}
@ -585,7 +585,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), halt.AsTracing(), tracing.GasChangeCallFailedExecution)
}
// EIP-8037 collision rule: the state reservoir is fully preserved on
// address collision while regular gas is burnt.
// address collision while execution gas is burnt.
return nil, common.Address{}, halt, ErrContractAddressCollision
}
// Create a new account on the state only if the object was not present.
@ -606,11 +606,11 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
}
// Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 {
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.ExecutionGas)
if consumed < wanted {
return nil, common.Address{}, gas.ExitHalt(), ErrOutOfGas
}
prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
prior, _ := gas.Charge(GasCosts{ExecutionGas: consumed})
if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractInit)
}
@ -659,8 +659,8 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
return ret, ErrInvalidCode
}
if evm.chainRules.IsEIP4762 {
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.RegularGas)
contract.chargeRegular(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas.ExecutionGas)
contract.chargeExecution(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
if len(ret) > 0 && (consumed < wanted) {
return ret, ErrCodeStoreOutOfGas
}
@ -673,9 +673,9 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
if err := CheckMaxCodeSize(&evm.chainRules, uint64(len(ret))); err != nil {
return ret, err
}
// Charge regular gas (hash cost) before state gas.
regularCost := toWordSize(uint64(len(ret))) * params.Keccak256WordGas
if !contract.chargeRegular(regularCost, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
// Charge execution gas (hash cost) before state gas.
executionCost := toWordSize(uint64(len(ret))) * params.Keccak256WordGas
if !contract.chargeExecution(executionCost, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
return ret, ErrCodeStoreOutOfGas
}
// Charge state gas (code-deposit) afterwards.
@ -685,7 +685,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
}
} else {
createDataCost := uint64(len(ret)) * params.CreateDataGas
if !contract.chargeRegular(createDataCost, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
if !contract.chargeExecution(createDataCost, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
return ret, ErrCodeStoreOutOfGas
}
if err := CheckMaxCodeSize(&evm.chainRules, uint64(len(ret))); err != nil {
@ -749,7 +749,7 @@ func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas GasBudget, value *big.Int) {
tracer := evm.Config.Tracer
if tracer.OnEnter != nil {
tracer.OnEnter(depth, byte(typ), from, to, input, startGas.RegularGas, value)
tracer.OnEnter(depth, byte(typ), from, to, input, startGas.ExecutionGas, value)
}
if tracer.HasGasHook() {
tracer.EmitGasChange(tracing.Gas{}, startGas.AsTracing(), tracing.GasChangeCallInitialBalance)
@ -769,7 +769,7 @@ func (evm *EVM) captureEnd(depth int, startGas GasBudget, leftOverGas GasBudget,
reverted = false
}
if tracer.OnExit != nil {
tracer.OnExit(depth, ret, startGas.RegularGas-leftOverGas.RegularGas, VMErrorFromErr(err), reverted)
tracer.OnExit(depth, ret, startGas.ExecutionGas-leftOverGas.ExecutionGas, VMErrorFromErr(err), reverted)
}
}

View file

@ -83,7 +83,7 @@ func memoryCopierGas(stackpos int) gasFunc {
if gas, overflow = math.SafeAdd(gas, words); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
}
@ -114,12 +114,12 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// 3. From a non-zero to a non-zero (CHANGE)
switch {
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
return GasCosts{RegularGas: params.SstoreSetGas}, nil
return GasCosts{ExecutionGas: params.SstoreSetGas}, nil
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas)
return GasCosts{RegularGas: params.SstoreClearGas}, nil
return GasCosts{ExecutionGas: params.SstoreClearGas}, nil
default: // non 0 => non 0 (or 0 => 0)
return GasCosts{RegularGas: params.SstoreResetGas}, nil
return GasCosts{ExecutionGas: params.SstoreResetGas}, nil
}
}
@ -139,16 +139,16 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return GasCosts{RegularGas: params.NetSstoreNoopGas}, nil
return GasCosts{ExecutionGas: params.NetSstoreNoopGas}, nil
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return GasCosts{RegularGas: params.NetSstoreInitGas}, nil
return GasCosts{ExecutionGas: params.NetSstoreInitGas}, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
return GasCosts{RegularGas: params.NetSstoreCleanGas}, nil // write existing slot (2.1.2)
return GasCosts{ExecutionGas: params.NetSstoreCleanGas}, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
@ -164,7 +164,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
}
}
return GasCosts{RegularGas: params.NetSstoreDirtyGas}, nil
return GasCosts{ExecutionGas: params.NetSstoreDirtyGas}, nil
}
// Here come the EIP2200 rules:
@ -187,7 +187,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
return GasCosts{}, ErrWriteProtection
}
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
if contract.Gas.ExecutionGas <= params.SstoreSentryGasEIP2200 {
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
@ -198,16 +198,16 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil
return GasCosts{ExecutionGas: params.SloadGasEIP2200}, nil
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return GasCosts{RegularGas: params.SstoreSetGasEIP2200}, nil
return GasCosts{ExecutionGas: params.SstoreSetGasEIP2200}, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
}
return GasCosts{RegularGas: params.SstoreResetGasEIP2200}, nil // write existing slot (2.1.2)
return GasCosts{ExecutionGas: params.SstoreResetGasEIP2200}, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
@ -223,7 +223,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
}
}
return GasCosts{RegularGas: params.SloadGasEIP2200}, nil // dirty update (2.2)
return GasCosts{ExecutionGas: params.SloadGasEIP2200}, nil // dirty update (2.2)
}
func makeGasLog(n uint64) gasFunc {
@ -252,7 +252,7 @@ func makeGasLog(n uint64) gasFunc {
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
}
@ -271,7 +271,7 @@ func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
// pureMemoryGascost is used by several operations, which aside from their
@ -282,7 +282,7 @@ func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if err != nil {
return GasCosts{}, err
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
var (
@ -318,7 +318,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -341,7 +341,7 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -364,7 +364,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -377,7 +377,7 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -390,7 +390,7 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
var (
@ -406,7 +406,7 @@ func makeCallVariantGasCost(intrinsicFunc intrinsicGasFunc) gasFunc {
if err != nil {
return GasCosts{}, err
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic, stack.back(0))
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.ExecutionGas, intrinsic, stack.back(0))
if err != nil {
return GasCosts{}, err
}
@ -414,7 +414,7 @@ func makeCallVariantGasCost(intrinsicFunc intrinsicGasFunc) gasFunc {
if overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
}
@ -442,7 +442,7 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
}
// Terminate the gas measurement if the leftover gas is not sufficient,
// it can effectively prevent accessing the states in the following steps.
if contract.Gas.RegularGas < gas {
if contract.Gas.ExecutionGas < gas {
return 0, ErrOutOfGas
}
// Stateful check
@ -538,7 +538,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
if !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -563,7 +563,7 @@ func gasCreateEip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
// The account-creation state gas is not part of the opcode cost: it is
// charged conditionally at the destination access, in the creating frame,
// right before the 63/64ths split (see opCreate).
return GasCosts{RegularGas: gas + wordGas}, nil
return GasCosts{ExecutionGas: gas + wordGas}, nil
}
func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -591,14 +591,14 @@ func gasCreate2Eip8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
// The account-creation state gas is not part of the opcode cost: it is
// charged conditionally at the destination access, in the creating frame,
// right before the 63/64ths split (see opCreate2).
return GasCosts{RegularGas: gas + wordGas}, nil
return GasCosts{ExecutionGas: gas + wordGas}, nil
}
// regularGasCall8038 is the intrinsic regular-gas calculator for CALL in
// executionGasCall8038 is the intrinsic execution-gas calculator for CALL in
// Amsterdam. It computes memory expansion plus the re-priced CALL_VALUE
// (ACCOUNT_WRITE + CALL_STIPEND) on value transfers, but excludes new account
// creation, which is handled as state gas by stateGasCall8037.
func regularGasCall8038(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func executionGasCall8038(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
transfersValue = !stack.back(2).IsZero()
@ -656,10 +656,10 @@ func gasSelfdestruct8037And8038(evm *EVM, contract *Contract, stack *Stack, mem
if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back.
evm.StateDB.AddAddressToAccessList(address)
gas.RegularGas = params.ColdAccountAccessAmsterdam
gas.ExecutionGas = params.ColdAccountAccessAmsterdam
}
// Check we have enough regular gas before we add the address to the BAL.
if contract.Gas.RegularGas < gas.RegularGas {
// Check we have enough execution gas before we add the address to the BAL.
if contract.Gas.ExecutionGas < gas.ExecutionGas {
return gas, ErrOutOfGas
}
// Important: use StateDB.Empty instead of !StateDB.Exist. An account may exist
@ -669,7 +669,7 @@ func gasSelfdestruct8037And8038(evm *EVM, contract *Contract, stack *Stack, mem
//
// Funding such an account makes it permanent state growth and must be charged.
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
gas.RegularGas += params.AccountWriteAmsterdam
gas.ExecutionGas += params.AccountWriteAmsterdam
gas.StateGas += params.AccountCreationSize * evm.Context.CostPerStateByte
}
return gas, nil
@ -682,7 +682,7 @@ func gasSStore8037And8038(evm *EVM, contract *Contract, stack *Stack, mem *Memor
return GasCosts{}, ErrWriteProtection
}
// If we fail the minimum gas availability invariant, fail (0).
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
if contract.Gas.ExecutionGas <= params.SstoreSentryGasEIP2200 {
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
}
var (
@ -697,7 +697,7 @@ func gasSStore8037And8038(evm *EVM, contract *Contract, stack *Stack, mem *Memor
access = params.ColdStorageAccessAmsterdam
}
// Check access cost affordability before reading slot
if contract.Gas.RegularGas < access {
if contract.Gas.ExecutionGas < access {
return GasCosts{}, errors.New("not enough gas for slot access")
}
if !slotPresent {
@ -709,19 +709,19 @@ func gasSStore8037And8038(evm *EVM, contract *Contract, stack *Stack, mem *Memor
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
)
if current == value { // noop (1)
return GasCosts{RegularGas: access}, nil
return GasCosts{ExecutionGas: access}, nil
}
if original == current { // first change of the slot (2.1)
if original == (common.Hash{}) { // create slot (2.1.1)
return GasCosts{
RegularGas: access + params.StorageWriteAmsterdam,
StateGas: stateSet,
ExecutionGas: access + params.StorageWriteAmsterdam,
StateGas: stateSet,
}, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.StorageClearRefundAmsterdam)
}
return GasCosts{RegularGas: access + params.StorageWriteAmsterdam}, nil // write existing slot (2.1.2)
return GasCosts{ExecutionGas: access + params.StorageWriteAmsterdam}, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
@ -736,5 +736,5 @@ func gasSStore8037And8038(evm *EVM, contract *Contract, stack *Stack, mem *Memor
}
evm.StateDB.AddRefund(params.StorageWriteAmsterdam)
}
return GasCosts{RegularGas: access}, nil // dirty update (2.2)
return GasCosts{ExecutionGas: access}, nil // dirty update (2.2)
}

View file

@ -26,35 +26,35 @@ import (
// GasCosts denotes a vector of gas costs in the multidimensional metering
// paradigm. It represents the cost charged by an individual operation.
type GasCosts struct {
RegularGas uint64
StateGas uint64
ExecutionGas uint64
StateGas uint64
}
// Sum returns the total gas (regular + state).
// Sum returns the total gas (execution + state).
func (g GasCosts) Sum() uint64 {
return g.RegularGas + g.StateGas
return g.ExecutionGas + g.StateGas
}
// String returns a visual representation of the gas vector.
func (g GasCosts) String() string {
return fmt.Sprintf("<%v,%v>", g.RegularGas, g.StateGas)
return fmt.Sprintf("<%v,%v>", g.ExecutionGas, g.StateGas)
}
// GasBudget is the unified gas-state structure used throughout the EVM.
// It carries two pairs of fields:
//
// - RegularGas / StateGas: the running balance during execution, or the
// - ExecutionGas / StateGas: the running balance during execution, or the
// leftover balance the caller must absorb after a sub-call.
// - UsedRegularGas / UsedStateGas: per-frame accumulators tracking gross
// - UsedExecutionGas / UsedStateGas: per-frame accumulators tracking gross
// consumption. UsedStateGas is signed so it can be decremented by inline
// state-gas refunds (e.g., SSTORE 0->A->0).
type GasBudget struct {
RegularGas uint64 // remaining regular-gas balance (or leftover for caller to absorb)
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
UsedRegularGas uint64 // gross regular gas consumed in this frame
UsedStateGas int64 // signed net state-gas consumed in this frame
ExecutionGas uint64 // remaining execution-gas balance (or leftover for caller to absorb)
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
UsedExecutionGas uint64 // gross execution gas consumed in this frame
UsedStateGas int64 // signed net state-gas consumed in this frame
// Spilled tracks how much of this frame's regular gas (gas_left)
// Spilled tracks how much of this frame's execution gas (gas_left)
// has been borrowed to cover state-gas charges that exceeded the
// reservoir.
Spilled uint64
@ -62,21 +62,21 @@ type GasBudget struct {
// NewGasBudget initializes a fresh GasBudget for execution / forwarding,
// with both usage accumulators set to zero.
func NewGasBudget(regular, state uint64) GasBudget {
return GasBudget{RegularGas: regular, StateGas: state}
func NewGasBudget(execution, state uint64) GasBudget {
return GasBudget{ExecutionGas: execution, StateGas: state}
}
// Used returns the total scalar gas consumed relative to an initial budget.
func (g GasBudget) Used(initial GasBudget) uint64 {
return (initial.RegularGas + initial.StateGas) - (g.RegularGas + g.StateGas)
return (initial.ExecutionGas + initial.StateGas) - (g.ExecutionGas + g.StateGas)
}
// String returns a visual representation of the budget.
func (g GasBudget) String() string {
return fmt.Sprintf("<%v,%v,used=<%v,%v>,borrowed=%v>", g.RegularGas, g.StateGas, g.UsedRegularGas, g.UsedStateGas, g.Spilled)
return fmt.Sprintf("<%v,%v,used=<%v,%v>,borrowed=%v>", g.ExecutionGas, g.StateGas, g.UsedExecutionGas, g.UsedStateGas, g.Spilled)
}
// Charge deducts a combined regular+state cost from the running balance and
// Charge deducts a combined execution+state cost from the running balance and
// updates the usage accumulators.
func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
prior := *g
@ -84,53 +84,53 @@ func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
return prior, ok
}
// ChargeRegularOnly deducts a regular-only cost. It's always preferred for
// ChargeExecutionOnly deducts a execution-only cost. It's always preferred for
// performance consideration if the opcode doesn't have any state cost.
func (g *GasBudget) ChargeRegularOnly(r uint64) bool {
if g.RegularGas < r {
func (g *GasBudget) ChargeExecutionOnly(r uint64) bool {
if g.ExecutionGas < r {
return false
}
g.RegularGas -= r
g.UsedRegularGas += r
g.ExecutionGas -= r
g.UsedExecutionGas += r
return true
}
// CanAfford reports whether the running budget can cover the given cost vector
// without going out of gas.
func (g GasBudget) CanAfford(cost GasCosts) bool {
if g.RegularGas < cost.RegularGas {
if g.ExecutionGas < cost.ExecutionGas {
return false
}
regular := g.RegularGas - cost.RegularGas
execution := g.ExecutionGas - cost.ExecutionGas
if cost.StateGas > g.StateGas {
return cost.StateGas-g.StateGas <= regular
return cost.StateGas-g.StateGas <= execution
}
return true
}
// charge deducts both the state and regular cost.
// charge deducts both the state and execution cost.
func (g *GasBudget) charge(cost GasCosts) bool {
if g.RegularGas < cost.RegularGas {
if g.ExecutionGas < cost.ExecutionGas {
return false
}
regular := g.RegularGas - cost.RegularGas
execution := g.ExecutionGas - cost.ExecutionGas
state := g.StateGas
spilled := g.Spilled
if cost.StateGas > state {
spillover := cost.StateGas - state
if spillover > regular {
if spillover > execution {
return false
}
regular -= spillover
execution -= spillover
state = 0
spilled += spillover
} else {
state -= cost.StateGas
}
g.RegularGas = regular
g.ExecutionGas = execution
g.StateGas = state
g.UsedRegularGas += cost.RegularGas
g.UsedExecutionGas += cost.ExecutionGas
g.UsedStateGas += int64(cost.StateGas)
g.Spilled = spilled
return true
@ -138,12 +138,12 @@ func (g *GasBudget) charge(cost GasCosts) bool {
// AsTracing converts the GasBudget into the tracing-facing Gas vector.
func (g GasBudget) AsTracing() tracing.Gas {
return tracing.Gas{Regular: g.RegularGas, State: g.StateGas}
return tracing.Gas{Execution: g.ExecutionGas, State: g.StateGas}
}
// ChargeRegular is a convenience that deducts a regular-only cost.
func (g *GasBudget) ChargeRegular(r uint64) (GasBudget, bool) {
return g.Charge(GasCosts{RegularGas: r})
// ChargeExecution is a convenience that deducts a execution-only cost.
func (g *GasBudget) ChargeExecution(r uint64) (GasBudget, bool) {
return g.Charge(GasCosts{ExecutionGas: r})
}
// ChargeState is a convenience that deducts a state-only cost.
@ -153,45 +153,45 @@ func (g *GasBudget) ChargeState(s uint64) (GasBudget, bool) {
// IsZero returns an indicator if the gas budget has been exhausted.
func (g *GasBudget) IsZero() bool {
return g.RegularGas == 0 && g.StateGas == 0
return g.ExecutionGas == 0 && g.StateGas == 0
}
// RefundState applies an inline state-gas refund (e.g., SSTORE 0->A->0).
func (g *GasBudget) RefundState(s uint64) {
repay := min(s, g.Spilled)
g.RegularGas += repay
g.ExecutionGas += repay
g.Spilled -= repay
g.StateGas += s - repay
g.UsedStateGas -= int64(s)
}
// DrainRegular burns the remaining regular-gas.
func (g *GasBudget) DrainRegular() {
g.UsedRegularGas += g.RegularGas
g.RegularGas = 0
// DrainExecution burns the remaining execution-gas.
func (g *GasBudget) DrainExecution() {
g.UsedExecutionGas += g.ExecutionGas
g.ExecutionGas = 0
}
// Forward drains `regular` regular gas and the entire state reservoir from
// Forward drains `execution` gas and the entire state reservoir from
// the parent's running budget and returns the initial GasBudget for a child
// frame. The parent's UsedRegularGas is bumped by the forwarded amount so
// frame. The parent's UsedExecutionGas is bumped by the forwarded amount so
// that the absorb-on-return path correctly reclaims the unused portion.
func (g *GasBudget) Forward(regular uint64) GasBudget {
g.RegularGas -= regular
g.UsedRegularGas += regular
func (g *GasBudget) Forward(execution uint64) GasBudget {
g.ExecutionGas -= execution
g.UsedExecutionGas += execution
child := GasBudget{
RegularGas: regular,
StateGas: g.StateGas,
ExecutionGas: execution,
StateGas: g.StateGas,
}
g.StateGas = 0
return child
}
// ForwardAll forwards the parent's full remaining budget (both regular and
// state) to a child frame. Equivalent to Forward(g.RegularGas) — used at
// ForwardAll forwards the parent's full remaining budget (both execution and
// state) to a child frame. Equivalent to Forward(g.ExecutionGas) — used at
// the tx boundary where there is no 1/64 retention.
func (g *GasBudget) ForwardAll() GasBudget {
return g.Forward(g.RegularGas)
return g.Forward(g.ExecutionGas)
}
// ============================================================================
@ -207,7 +207,7 @@ func (g GasBudget) ExitSuccess() GasBudget {
// ExitRevert produces the leftover for a REVERT exit. The frame's state
// changes are discarded, so all state gas it charged is refilled with LIFO
// mechanism: up to Spilled is returned to RegularGas (the regular gas it
// mechanism: up to Spilled is returned to ExecutionGas (the execution gas it
// borrowed), and the remainder restores the reservoir.
func (g GasBudget) ExitRevert() GasBudget {
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.Spilled)
@ -218,19 +218,19 @@ func (g GasBudget) ExitRevert() GasBudget {
log.Warn("Negative reservoir at revert", "remaining", g.StateGas, "used", g.UsedStateGas, "borrowed", g.Spilled)
}
return GasBudget{
RegularGas: g.RegularGas + g.Spilled,
StateGas: uint64(reservoir),
UsedRegularGas: g.UsedRegularGas,
UsedStateGas: 0,
Spilled: 0,
ExecutionGas: g.ExecutionGas + g.Spilled,
StateGas: uint64(reservoir),
UsedExecutionGas: g.UsedExecutionGas,
UsedStateGas: 0,
Spilled: 0,
}
}
// ExitHalt produces the leftover for an exceptional halt. As with a revert, the
// frame's state changes are rolled back and its state gas is refilled with LIFO
// mechanism. The difference is that the frame's regular gas is consumed rather
// than returned. The portion refilled to RegularGas is therefore burned along
// with the rest of regular gas, leaving only the reservoir portion to survive,
// mechanism. The difference is that the frame's execution gas is consumed rather
// than returned. The portion refilled to ExecutionGas is therefore burned along
// with the rest of execution gas, leaving only the reservoir portion to survive,
// which equals the reservoir's value at the start of the frame.
func (g GasBudget) ExitHalt() GasBudget {
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.Spilled)
@ -241,11 +241,11 @@ func (g GasBudget) ExitHalt() GasBudget {
log.Warn("Negative reservoir at halt", "remaining", g.StateGas, "used", g.UsedStateGas, "borrowed", g.Spilled)
}
return GasBudget{
RegularGas: 0,
StateGas: uint64(reservoir),
UsedRegularGas: g.UsedRegularGas + g.RegularGas + g.Spilled,
UsedStateGas: 0,
Spilled: 0,
ExecutionGas: 0,
StateGas: uint64(reservoir),
UsedExecutionGas: g.UsedExecutionGas + g.ExecutionGas + g.Spilled,
UsedStateGas: 0,
Spilled: 0,
}
}
@ -268,14 +268,14 @@ func (g GasBudget) Exit(err error) GasBudget {
// Absorb merges a sub-call's leftover GasBudget into this (caller's) running
// budget. Additionally, it does an EIP-8037 spillover correction:
// state-gas that spilled into the regular pool inside the child frame is
// excluded from the UsedRegularGas.
// state-gas that spilled into the execution pool inside the child frame is
// excluded from the UsedExecutionGas.
func (g *GasBudget) Absorb(child GasBudget) {
g.UsedRegularGas -= child.RegularGas
g.RegularGas += child.RegularGas
g.UsedExecutionGas -= child.ExecutionGas
g.ExecutionGas += child.ExecutionGas
g.StateGas = child.StateGas
g.UsedStateGas += child.UsedStateGas
g.UsedRegularGas -= child.Spilled
g.UsedExecutionGas -= child.Spilled
g.Spilled += child.Spilled
}

View file

@ -546,7 +546,7 @@ func opMsize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
func opGas(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
scope.Stack.get().SetUint64(scope.Contract.Gas.RegularGas)
scope.Stack.get().SetUint64(scope.Contract.Gas.ExecutionGas)
return nil, nil
}
@ -641,8 +641,8 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if halt {
return nil, err
}
// Apply EIP-150 to the regular gas left after the state charge.
forward := scope.Contract.Gas.RegularGas
// Apply EIP-150 to the execution gas left after the state charge.
forward := scope.Contract.Gas.ExecutionGas
if evm.chainRules.IsEIP150 {
forward -= forward / 64
}
@ -697,8 +697,8 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if halt {
return nil, err
}
// Apply EIP-150 to the regular gas left after the state charge.
forward := scope.Contract.Gas.RegularGas
// Apply EIP-150 to the execution gas left after the state charge.
forward := scope.Contract.Gas.ExecutionGas
forward -= forward / 64
// reuse size int for stackvalue
@ -750,7 +750,7 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
gas += params.CallStipend
}
// Regular gas for the forward was already pre-deducted by the dynamic
// Execution gas for the forward was already pre-deducted by the dynamic
// gas table (see makeCallVariantGasCallEIP*); only the state reservoir
// needs to be handed off to the child here.
childBudget := NewGasBudget(gas, scope.Contract.Gas.StateGas)
@ -792,7 +792,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if !value.IsZero() {
gas += params.CallStipend
}
// Regular gas for the forward was already pre-deducted by the dynamic
// Execution gas for the forward was already pre-deducted by the dynamic
// gas table, only the state reservoir needs to be handed off to the
// child here.
childBudget := NewGasBudget(gas, scope.Contract.Gas.StateGas)
@ -825,7 +825,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// Get arguments from the memory.
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
// Regular gas for the forward was already pre-deducted by the dynamic
// Execution gas for the forward was already pre-deducted by the dynamic
// gas table, only the state reservoir needs to be handed off to the
// child here.
childBudget := NewGasBudget(gas, scope.Contract.Gas.StateGas)
@ -857,7 +857,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// Get arguments from the memory.
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
// Regular gas for the forward was already pre-deducted by the dynamic
// Execution gas for the forward was already pre-deducted by the dynamic
// gas table, only the state reservoir needs to be handed off to the
// child here.
childBudget := NewGasBudget(gas, scope.Contract.Gas.StateGas)

View file

@ -897,7 +897,7 @@ func TestOpMCopy(t *testing.T) {
if dynamicCost, err := gasMcopy(evm, nil, stack, mem, memorySize); err != nil {
t.Error(err)
} else {
haveGas = GasFastestStep + dynamicCost.RegularGas
haveGas = GasFastestStep + dynamicCost.ExecutionGas
}
// Expand mem
if memorySize > 0 {

View file

@ -167,15 +167,15 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
for {
if debug {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas.RegularGas
logged, pcCopy, gasCopy = false, pc, contract.Gas.ExecutionGas
}
if isEIP4762 && !contract.IsDeployment && !contract.IsSystemCall {
// if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs.
contractAddr := contract.Address()
consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas.RegularGas)
contract.chargeRegular(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas.ExecutionGas)
contract.chargeExecution(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
if consumed < wanted {
return nil, ErrOutOfGas
}
@ -193,7 +193,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
// for tracing: this gas consumption event is emitted below in the debug section.
if !contract.Gas.ChargeRegularOnly(cost) {
if !contract.Gas.ChargeExecutionOnly(cost) {
return nil, ErrOutOfGas
}
@ -219,12 +219,12 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
// cost is explicitly set so that the capture state defer method can get the proper cost
var dynamicCost GasCosts
dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize)
cost += dynamicCost.RegularGas // for tracing
cost += dynamicCost.ExecutionGas // for tracing
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
}
if dynamicCost.StateGas == 0 {
if !contract.Gas.ChargeRegularOnly(dynamicCost.RegularGas) {
if !contract.Gas.ChargeExecutionOnly(dynamicCost.ExecutionGas) {
return nil, ErrOutOfGas
}
} else if !contract.Gas.charge(dynamicCost) {
@ -236,8 +236,8 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
if debug {
if evm.Config.Tracer.HasGasHook() {
evm.Config.Tracer.EmitGasChange(
tracing.Gas{Regular: gasCopy, State: contract.Gas.StateGas},
tracing.Gas{Regular: gasCopy - cost, State: contract.Gas.StateGas},
tracing.Gas{Execution: gasCopy, State: contract.Gas.StateGas},
tracing.Gas{Execution: gasCopy - cost, State: contract.Gas.StateGas},
tracing.GasChangeCallOpCode,
)
}

View file

@ -29,8 +29,8 @@ type (
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) (size uint64, overflow bool)
regularGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error)
stateGasFunc func(*EVM, *Contract, *Stack) (uint64, error)
executionGasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error)
stateGasFunc func(*EVM, *Contract, *Stack) (uint64, error)
)
type operation struct {

View file

@ -32,7 +32,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
return GasCosts{}, ErrWriteProtection
}
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas.RegularGas <= params.SstoreSentryGasEIP2200 {
if contract.Gas.ExecutionGas <= params.SstoreSentryGasEIP2200 {
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
@ -53,18 +53,18 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
return GasCosts{ExecutionGas: cost + params.WarmStorageReadCostEIP2929}, nil // SLOAD_GAS
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return GasCosts{RegularGas: cost + params.SstoreSetGasEIP2200}, nil
return GasCosts{ExecutionGas: cost + params.SstoreSetGasEIP2200}, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(clearingRefund)
}
// EIP-2200 original clause:
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
return GasCosts{RegularGas: cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil // write existing slot (2.1.2)
return GasCosts{ExecutionGas: cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929)}, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
@ -89,7 +89,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)
return GasCosts{RegularGas: cost + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
return GasCosts{ExecutionGas: cost + params.WarmStorageReadCostEIP2929}, nil // dirty update (2.2)
}
}
@ -103,9 +103,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
slot := common.Hash(loc.Bytes32())
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
return GasCosts{RegularGas: params.ColdSloadCostEIP2929}, nil
return GasCosts{ExecutionGas: params.ColdSloadCostEIP2929}, nil
}
return GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}, nil
return GasCosts{ExecutionGas: params.WarmStorageReadCostEIP2929}, nil
}
// gasSLoad8038 mirrors gasSLoadEIP2929 but uses the EIP-8038 COLD_STORAGE_ACCESS
@ -115,9 +115,9 @@ func gasSLoad8038(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
slot := common.Hash(loc.Bytes32())
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
return GasCosts{RegularGas: params.ColdStorageAccessAmsterdam}, nil
return GasCosts{ExecutionGas: params.ColdStorageAccessAmsterdam}, nil
}
return GasCosts{RegularGas: params.WarmStorageReadCostEIP2929}, nil
return GasCosts{ExecutionGas: params.WarmStorageReadCostEIP2929}, nil
}
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
@ -131,7 +131,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
if err != nil {
return GasCosts{}, err
}
gas := gasCost.RegularGas
gas := gasCost.ExecutionGas
addr := common.Address(stack.peek().Bytes20())
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
@ -141,9 +141,9 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
// gasExtCodeCopy8038 mirrors gasExtCodeCopyEIP2929 but uses the EIP-8038
@ -155,7 +155,7 @@ func gasExtCodeCopy8038(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if err != nil {
return GasCosts{}, err
}
gas := gasCost.RegularGas
gas := gasCost.ExecutionGas
addr := common.Address(stack.peek().Bytes20())
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
@ -171,7 +171,7 @@ func gasExtCodeCopy8038(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
// gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list.
@ -188,7 +188,7 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(addr)
// The warm storage read cost is already charged as constantGas
return GasCosts{RegularGas: params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929}, nil
return GasCosts{ExecutionGas: params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929}, nil
}
return GasCosts{}, nil
}
@ -202,7 +202,7 @@ func gasEip8038AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(addr)
// The warm storage read cost is already charged as constantGas
return GasCosts{RegularGas: params.ColdAccountAccessAmsterdam - params.WarmStorageReadCostEIP2929}, nil
return GasCosts{ExecutionGas: params.ColdAccountAccessAmsterdam - params.WarmStorageReadCostEIP2929}, nil
}
return GasCosts{}, nil
}
@ -215,7 +215,7 @@ func gasExtCodeSize8038(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
return GasCosts{}, err
}
// Additional WARM_ACCESS for the second database read (contract size).
cost.RegularGas += params.WarmStorageReadCostEIP2929
cost.ExecutionGas += params.WarmStorageReadCostEIP2929
return cost, nil
}
@ -231,7 +231,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.chargeRegular(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
if !contract.chargeExecution(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
}
}
@ -248,14 +248,14 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
// add it to the returned gas. By adding it to the return, it will be charged
// outside of this function, as part of the dynamic gas, and that will make it
// also become correctly reported to tracers.
contract.Gas.RegularGas += coldCost
contract.Gas.ExecutionGas += coldCost
gas := gasCost.RegularGas
gas := gasCost.ExecutionGas
var overflow bool
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
}
@ -304,7 +304,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
// Terminate the gas measurement if the leftover gas is not sufficient,
// it can effectively prevent accessing the states in the following steps
if contract.Gas.RegularGas < gas {
if contract.Gas.ExecutionGas < gas {
return GasCosts{}, ErrOutOfGas
}
}
@ -315,7 +315,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
return gasFunc
}
@ -348,7 +348,7 @@ func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
}
var (
innerGasCall8038 = makeCallVariantGasCallEIP8037(regularGasCall8038, stateGasCall8037, params.ColdAccountAccessAmsterdam)
innerGasCall8038 = makeCallVariantGasCallEIP8037(executionGasCall8038, stateGasCall8037, params.ColdAccountAccessAmsterdam)
gasCallCode8038 = makeCallVariantGasCallEIP7702(gasCallCodeIntrinsic8038, params.ColdAccountAccessAmsterdam)
gasDelegateCall8038 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic, params.ColdAccountAccessAmsterdam)
gasStaticCall8038 = makeCallVariantGasCallEIP7702(gasStaticCallIntrinsic, params.ColdAccountAccessAmsterdam)
@ -383,7 +383,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
// Charge the remaining difference here already, to correctly calculate
// available gas for call
if !contract.chargeRegular(eip2929Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
if !contract.chargeExecution(eip2929Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
}
}
@ -400,7 +400,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
// Terminate the gas measurement if the leftover gas is not sufficient,
// it can effectively prevent accessing the states in the following steps.
// It's an essential safeguard before any stateful check.
if !contract.chargeRegular(intrinsicCost, evm.Config.Tracer, tracing.GasChangeIgnored) {
if !contract.chargeExecution(intrinsicCost, evm.Config.Tracer, tracing.GasChangeIgnored) {
return GasCosts{}, ErrOutOfGas
}
@ -412,7 +412,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
evm.StateDB.AddAddressToAccessList(target)
eip7702Cost = coldCost
}
if !contract.chargeRegular(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
if !contract.chargeExecution(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
}
// The delegated address has passed its gas check; record it in the
@ -422,7 +422,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
}
// Calculate the gas budget for the nested call. The costs defined by
// EIP-2929 and EIP-7702 have already been applied.
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, 0, stack.back(0))
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.ExecutionGas, 0, stack.back(0))
if err != nil {
return GasCosts{}, err
}
@ -430,13 +430,13 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
// adding it to the return, it will be charged outside of this function, as
// part of the dynamic gas. This will ensure it is correctly reported to
// tracers.
contract.Gas.RegularGas += eip2929Cost + eip7702Cost + intrinsicCost
contract.Gas.ExecutionGas += eip2929Cost + eip7702Cost + intrinsicCost
// Undo the RegularGasUsed increments from the direct UseGas charges,
// Undo the ExecutionGasUsed increments from the direct UseGas charges,
// since this gas will be re-charged via the returned cost.
contract.Gas.UsedRegularGas -= eip2929Cost
contract.Gas.UsedRegularGas -= eip7702Cost
contract.Gas.UsedRegularGas -= intrinsicCost
contract.Gas.UsedExecutionGas -= eip2929Cost
contract.Gas.UsedExecutionGas -= eip7702Cost
contract.Gas.UsedExecutionGas -= intrinsicCost
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
// the CALL opcode itself, and the cost incurred by nested calls.
@ -453,15 +453,15 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: totalCost}, nil
return GasCosts{ExecutionGas: totalCost}, nil
}
}
// makeCallVariantGasCallEIP8037 creates a call gas function for Amsterdam (EIP-8037).
// It extends the EIP-7702 pattern with state gas handling and GasUsed tracking.
// intrinsicFunc computes the regular gas (memory + transfer, no new account creation).
// intrinsicFunc computes the execution gas (memory + transfer, no new account creation).
// stateGasFunc computes the state gas (new account creation as state gas).
func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stateGasFunc, coldCost uint64) gasFunc {
func makeCallVariantGasCallEIP8037(executionFunc executionGasFunc, stateGasFunc stateGasFunc, coldCost uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
var (
eip2929Cost uint64
@ -472,21 +472,21 @@ func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stat
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
eip2929Cost = coldCost - params.WarmStorageReadCostEIP2929
if !contract.chargeRegular(eip2929Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
if !contract.chargeExecution(eip2929Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
}
}
// Compute regular cost (memory + transfer, no new account creation).
regularCost, err := regularFunc(evm, contract, stack, mem, memorySize)
// Compute execution cost (memory + transfer, no new account creation).
executionCost, err := executionFunc(evm, contract, stack, mem, memorySize)
if err != nil {
return GasCosts{}, err
}
// Charge intrinsic cost directly (regular gas). This must happen
// Charge intrinsic cost directly (execution gas). This must happen
// BEFORE state gas to prevent reservoir inflation, and also serves
// as the OOG guard before stateful operations.
if !contract.chargeRegular(regularCost, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
if !contract.chargeExecution(executionCost, evm.Config.Tracer, tracing.GasChangeCallOpCode) {
return GasCosts{}, ErrOutOfGas
}
@ -498,7 +498,7 @@ func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stat
evm.StateDB.AddAddressToAccessList(target)
eip7702Cost = coldCost
}
if !contract.chargeRegular(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
if !contract.chargeExecution(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
}
// The delegated address has passed its gas check; record it in the
@ -507,7 +507,7 @@ func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stat
recordDelegationAccess(evm, target)
}
// Compute and charge state gas (new account creation) AFTER regular gas.
// Compute and charge state gas (new account creation) AFTER execution gas.
stateGas, err := stateGasFunc(evm, contract, stack)
if err != nil {
return GasCosts{}, err
@ -519,15 +519,15 @@ func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stat
}
// Calculate the gas budget for the nested call (63/64 rule).
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, 0, stack.back(0))
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.ExecutionGas, 0, stack.back(0))
if err != nil {
return GasCosts{}, err
}
// Temporarily undo direct regular charges for tracer reporting.
// Temporarily undo direct execution charges for tracer reporting.
// The interpreter will charge the returned totalCost.
contract.Gas.RegularGas += eip2929Cost + eip7702Cost + regularCost
contract.Gas.UsedRegularGas -= eip2929Cost + eip7702Cost + regularCost
contract.Gas.ExecutionGas += eip2929Cost + eip7702Cost + executionCost
contract.Gas.UsedExecutionGas -= eip2929Cost + eip7702Cost + executionCost
// Aggregate total cost.
var (
@ -537,12 +537,12 @@ func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stat
if totalCost, overflow = math.SafeAdd(eip2929Cost, eip7702Cost); overflow {
return GasCosts{}, ErrGasUintOverflow
}
if totalCost, overflow = math.SafeAdd(totalCost, regularCost); overflow {
if totalCost, overflow = math.SafeAdd(totalCost, executionCost); overflow {
return GasCosts{}, ErrGasUintOverflow
}
if totalCost, overflow = math.SafeAdd(totalCost, evm.callGasTemp); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: totalCost}, nil
return GasCosts{ExecutionGas: totalCost}, nil
}
}

View file

@ -25,16 +25,16 @@ import (
)
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.RegularGas, true)}, nil
return GasCosts{ExecutionGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas.ExecutionGas, true)}, nil
}
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
return GasCosts{RegularGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.RegularGas, true)}, nil
return GasCosts{ExecutionGas: evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas.ExecutionGas, true)}, nil
}
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
address := stack.peek().Bytes20()
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
return GasCosts{ExecutionGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.ExecutionGas, true)}, nil
}
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -42,7 +42,7 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if _, isPrecompile := evm.precompile(address); isPrecompile {
return GasCosts{}, nil
}
return GasCosts{RegularGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.RegularGas, true)}, nil
return GasCosts{ExecutionGas: evm.AccessEvents.BasicDataGas(address, false, contract.Gas.ExecutionGas, true)}, nil
}
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -50,7 +50,7 @@ func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if _, isPrecompile := evm.precompile(address); isPrecompile {
return GasCosts{}, nil
}
return GasCosts{RegularGas: evm.AccessEvents.CodeHashGas(address, false, contract.Gas.RegularGas, true)}, nil
return GasCosts{ExecutionGas: evm.AccessEvents.CodeHashGas(address, false, contract.Gas.ExecutionGas, true)}, nil
}
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
@ -65,9 +65,9 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
// If value is transferred, it is charged before 1/64th
// is subtracted from the available gas pool.
if withTransferCosts && !stack.back(2).IsZero() {
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas)
if wantedValueTransferWitnessGas > contract.Gas.RegularGas {
return GasCosts{RegularGas: wantedValueTransferWitnessGas}, nil
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.ExecutionGas)
if wantedValueTransferWitnessGas > contract.Gas.ExecutionGas {
return GasCosts{ExecutionGas: wantedValueTransferWitnessGas}, nil
}
witnessGas = wantedValueTransferWitnessGas
} else if isPrecompile || isSystemContract {
@ -78,26 +78,26 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
// (so before we get to this point)
// But the message call is part of the subcall, for which only 63/64th
// of the gas should be available.
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas.RegularGas-witnessGas)
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas.ExecutionGas-witnessGas)
var overflow bool
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
if witnessGas > contract.Gas.RegularGas {
return GasCosts{RegularGas: witnessGas}, nil
if witnessGas > contract.Gas.ExecutionGas {
return GasCosts{ExecutionGas: witnessGas}, nil
}
}
contract.Gas.RegularGas -= witnessGas
contract.Gas.ExecutionGas -= witnessGas
// if the operation fails, adds witness gas to the gas before returning the error
gasCost, err := oldCalculator(evm, contract, stack, mem, memorySize)
contract.Gas.RegularGas += witnessGas // restore witness gas so that it can be charged at the callsite
gas := gasCost.RegularGas
contract.Gas.ExecutionGas += witnessGas // restore witness gas so that it can be charged at the callsite
gas := gasCost.ExecutionGas
var overflow bool
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, err
return GasCosts{ExecutionGas: gas}, err
}
}
@ -117,9 +117,9 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
return GasCosts{}, nil
}
contractAddr := contract.Address()
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas.RegularGas, false)
if wanted > contract.Gas.RegularGas {
return GasCosts{RegularGas: wanted}, nil
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas.ExecutionGas, false)
if wanted > contract.Gas.ExecutionGas {
return GasCosts{ExecutionGas: wanted}, nil
}
statelessGas := wanted
balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
@ -127,37 +127,37 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
if (isPrecompile || isSystemContract) && balanceIsZero {
return GasCosts{RegularGas: statelessGas}, nil
return GasCosts{ExecutionGas: statelessGas}, nil
}
if contractAddr != beneficiaryAddr {
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.RegularGas-statelessGas, false)
if wanted > contract.Gas.RegularGas-statelessGas {
return GasCosts{RegularGas: statelessGas + wanted}, nil
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas.ExecutionGas-statelessGas, false)
if wanted > contract.Gas.ExecutionGas-statelessGas {
return GasCosts{ExecutionGas: statelessGas + wanted}, nil
}
statelessGas += wanted
}
// Charge write costs if it transfers value
if !balanceIsZero {
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.RegularGas-statelessGas, false)
if wanted > contract.Gas.RegularGas-statelessGas {
return GasCosts{RegularGas: statelessGas + wanted}, nil
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas.ExecutionGas-statelessGas, false)
if wanted > contract.Gas.ExecutionGas-statelessGas {
return GasCosts{ExecutionGas: statelessGas + wanted}, nil
}
statelessGas += wanted
if contractAddr != beneficiaryAddr {
if evm.StateDB.Exist(beneficiaryAddr) {
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas, false)
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas.ExecutionGas-statelessGas, false)
} else {
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas.RegularGas-statelessGas)
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas.ExecutionGas-statelessGas)
}
if wanted > contract.Gas.RegularGas-statelessGas {
return GasCosts{RegularGas: statelessGas + wanted}, nil
if wanted > contract.Gas.ExecutionGas-statelessGas {
return GasCosts{ExecutionGas: statelessGas + wanted}, nil
}
statelessGas += wanted
}
}
return GasCosts{RegularGas: statelessGas}, nil
return GasCosts{ExecutionGas: statelessGas}, nil
}
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -165,7 +165,7 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if err != nil {
return GasCosts{}, err
}
gas := gasCost.RegularGas
gas := gasCost.ExecutionGas
if !contract.IsDeployment && !contract.IsSystemCall {
var (
codeOffset = stack.back(1)
@ -177,10 +177,10 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
}
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.RegularGas-gas)
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas.ExecutionGas-gas)
gas += wanted
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
@ -189,7 +189,7 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
if err != nil {
return GasCosts{}, err
}
gas := gasCost.RegularGas
gas := gasCost.ExecutionGas
addr := common.Address(stack.peek().Bytes20())
_, isPrecompile := evm.precompile(addr)
if isPrecompile || addr == params.HistoryStorageAddress {
@ -197,12 +197,12 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas.RegularGas-gas, true)
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas.ExecutionGas-gas, true)
var overflow bool
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
return GasCosts{}, ErrGasUintOverflow
}
return GasCosts{RegularGas: gas}, nil
return GasCosts{ExecutionGas: gas}, nil
}

View file

@ -156,7 +156,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
uint256.MustFromBig(cfg.Value),
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - result.RegularGas}, err)
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - result.ExecutionGas}, err)
}
return ret, cfg.State, err
}
@ -194,9 +194,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
uint256.MustFromBig(cfg.Value),
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - result.RegularGas}, err)
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - result.ExecutionGas}, err)
}
return code, address, result.RegularGas, err
return code, address, result.ExecutionGas, err
}
// Call executes the code given by the contract's address. It will return the
@ -233,7 +233,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
uint256.MustFromBig(cfg.Value),
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - result.RegularGas}, err)
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - result.ExecutionGas}, err)
}
return ret, result.RegularGas, err
return ret, result.ExecutionGas, err
}

View file

@ -66,9 +66,9 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice.ToBig()}), contract.Caller())
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig())
ret, err := evm.Run(contract, []byte{}, false)
tracer.OnExit(0, ret, startGas-contract.Gas.RegularGas, err, true)
tracer.OnExit(0, ret, startGas-contract.Gas.ExecutionGas, err, true)
// Rest gas assumes no refund
tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas.RegularGas}, nil)
tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas.ExecutionGas}, nil)
if err != nil {
return nil, err
}

View file

@ -115,7 +115,7 @@ func (t *muxTracer) OnGasChangeV2(old, new tracing.Gas, reason tracing.GasChange
if t.OnGasChangeV2 != nil {
t.OnGasChangeV2(old, new, reason)
} else if t.OnGasChange != nil {
t.OnGasChange(old.Regular, new.Regular, reason)
t.OnGasChange(old.Execution, new.Execution, reason)
}
}
}