core/vm: add access cost check

This commit is contained in:
Gary Rong 2026-06-30 10:29:54 +08:00
parent dd8dd1520f
commit b0b042e44c
2 changed files with 27 additions and 5 deletions

View file

@ -21,6 +21,7 @@
package vm
import (
"strings"
"testing"
"github.com/ethereum/go-ethereum/common"
@ -235,3 +236,19 @@ func TestEIP8038SelfdestructAccountWrite(t *testing.T) {
t.Fatalf("state gas = %d, want %d", res.UsedStateGas, want)
}
}
// TestEIP8038SStoreAccessGuard covers the affordability check that bails out
// before the slot is read once the gas left cannot cover the slot's access cost.
// The two PUSH1s cost 6, so a 2506 budget leaves 2500 at the SSTORE: above the
// reentrancy sentry (2300) yet below COLD_STORAGE_ACCESS (3000). The guard must
// fire, distinguishable from the sentry/charge OOG by its "slot access" message.
func TestEIP8038SStoreAccessGuard(t *testing.T) {
budget := NewGasBudget(6+params.SstoreSentryGasEIP2200+200, 0)
_, _, err := run8038(t, sstore(0, 1), budget, new(uint256.Int), nil)
if err == nil {
t.Fatal("expected failure: gas left cannot cover cold-slot access")
}
if !strings.Contains(err.Error(), "not enough gas for slot access") {
t.Fatalf("got %q, want the slot-access guard error", err)
}
}

View file

@ -696,10 +696,10 @@ func gasSStore8037And8038(evm *EVM, contract *Contract, stack *Stack, mem *Memor
return GasCosts{}, errors.New("not enough gas for reentrancy sentry")
}
var (
y, x = stack.back(1), stack.peek()
slot = common.Hash(x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
value = common.Hash(y.Bytes32())
y, x = stack.back(1), stack.peek()
slot = common.Hash(x.Bytes32())
value = common.Hash(y.Bytes32())
stateSet = params.StorageCreationSize * evm.Context.CostPerStateByte
)
// Check slot presence in the access list
access := params.WarmStorageReadCostEIP2929
@ -707,7 +707,12 @@ func gasSStore8037And8038(evm *EVM, contract *Contract, stack *Stack, mem *Memor
access = params.ColdStorageAccessAmsterdam
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
stateSet := params.StorageCreationSize * evm.Context.CostPerStateByte
// Check access cost affordability before reading slot
if contract.Gas.RegularGas < access {
return GasCosts{}, errors.New("not enough gas for slot access")
}
// Read the slot value for gas cost measurement
current, original := evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
if current == value { // noop (1)
return GasCosts{RegularGas: access}, nil