Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions core/vm/eip8038_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package vm

import (
"strings"
"testing"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -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)
}
}
15 changes: 10 additions & 5 deletions core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,18 +696,23 @@ 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
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
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
Expand Down
Loading