From 0a8f3b0177843877075cf3d26acadd2e70238745 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Tue, 25 Nov 2025 12:13:58 -0800 Subject: [PATCH] core/vm: for selfdestruct/sstore whose gas funcs are dependent on reading state, move readOnly call context check into gas func to avoid unecessary state reads in the gas handler in case where these are called in a static context. --- core/vm/instructions.go | 9 --------- core/vm/operations_acl.go | 8 ++++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index bff06faa5c..885d824a13 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -518,9 +518,6 @@ func opSload(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { } func opSstore(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { - if evm.readOnly { - return nil, ErrWriteProtection - } loc := scope.Stack.pop() val := scope.Stack.pop() evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) @@ -882,9 +879,6 @@ func opStop(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { } func opSelfdestruct(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { - if evm.readOnly { - return nil, ErrWriteProtection - } beneficiary := scope.Stack.pop() balance := evm.StateDB.GetBalance(scope.Contract.Address()) if scope.Contract.Address() != common.BytesToAddress(beneficiary.Bytes()) { @@ -903,9 +897,6 @@ func opSelfdestruct(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { } func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { - if evm.readOnly { - return nil, ErrWriteProtection - } beneficiary := scope.Stack.pop() balance := evm.StateDB.GetBalance(scope.Contract.Address()) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 085b018e4c..db53ede589 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -28,6 +28,9 @@ import ( func makeGasSStoreFunc(clearingRefund uint64) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + if evm.readOnly { + return 0, ErrWriteProtection + } // If we fail the minimum gas availability invariant, fail (0) if contract.Gas <= params.SstoreSentryGasEIP2200 { return 0, errors.New("not enough gas for reentrancy sentry") @@ -226,6 +229,11 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { gas uint64 address = common.Address(stack.peek().Bytes20()) ) + // TODO: EEST test that performs selfdestruct within a static context. no reads should be reported + // This is probably covered in the converted blockchain tests but need to double-check + if evm.readOnly { + return 0, ErrWriteProtection + } if !evm.StateDB.AddressInAccessList(address) { // If the caller cannot afford the cost, this change will be rolled back evm.StateDB.AddAddressToAccessList(address)