mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-05 11:33:47 +00:00
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:
parent
b483fe9e71
commit
6434bc91d4
26 changed files with 608 additions and 608 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue