Skip to content

Commit 7dbf994

Browse files
committed
refactor: set EVM.readOnly and depth before running stateful precompile
1 parent e35febe commit 7dbf994

File tree

2 files changed

+22
-34
lines changed

2 files changed

+22
-34
lines changed

core/vm/contracts.libevm.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,28 @@ func (t CallType) OpCode() OpCode {
120120
// run runs the [PrecompiledContract], differentiating between stateful and
121121
// regular types, updating `args.gasRemaining` in the stateful case.
122122
func (args *evmCallArgs) run(p PrecompiledContract, input []byte) (ret []byte, err error) {
123-
switch p := p.(type) {
124-
default:
123+
sp, ok := p.(statefulPrecompile)
124+
if !ok {
125125
return p.Run(input)
126-
case statefulPrecompile:
127-
env := args.env()
128-
ret, err := p(env, input)
129-
args.gasRemaining = env.Gas()
130-
return ret, err
131126
}
127+
128+
env := args.env()
129+
// Depth and read-only setting are handled by [EVMInterpreter.Run],
130+
// which isn't used for precompiles, so we need to do it ourselves to
131+
// maintain the expected invariants.
132+
in := env.evm.interpreter
133+
134+
in.evm.depth++
135+
defer func() { in.evm.depth-- }()
136+
137+
if env.callType == StaticCall && !in.readOnly {
138+
in.readOnly = true
139+
defer func() { in.readOnly = false }()
140+
}
141+
142+
ret, err = sp(env, input)
143+
args.gasRemaining = env.Gas()
144+
return ret, err
132145
}
133146

134147
// PrecompiledStatefulContract is the stateful equivalent of a

core/vm/environment.libevm.go

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,7 @@ func (e *environment) refundGas(add uint64) error {
6161
}
6262

6363
func (e *environment) ReadOnly() bool {
64-
// A switch statement provides clearer code coverage for difficult-to-test
65-
// cases.
66-
switch {
67-
case e.callType == StaticCall:
68-
// evm.interpreter.readOnly is only set to true via a call to
69-
// EVMInterpreter.Run() so, if a precompile is called directly with
70-
// StaticCall(), then readOnly might not be set yet.
71-
return true
72-
case e.evm.interpreter.readOnly:
73-
return true
74-
default:
75-
return false
76-
}
64+
return e.evm.interpreter.readOnly
7765
}
7866

7967
func (e *environment) Addresses() *libevm.AddressContext {
@@ -108,19 +96,6 @@ func (e *environment) Call(addr common.Address, input []byte, gas uint64, value
10896
}
10997

11098
func (e *environment) callContract(typ CallType, addr common.Address, input []byte, gas uint64, value *uint256.Int, opts ...CallOption) (retData []byte, retErr error) {
111-
// Depth and read-only setting are handled by [EVMInterpreter.Run], which
112-
// isn't used for precompiles, so we need to do it ourselves to maintain the
113-
// expected invariants.
114-
in := e.evm.interpreter
115-
116-
in.evm.depth++
117-
defer func() { in.evm.depth-- }()
118-
119-
if e.ReadOnly() && !in.readOnly { // i.e. the precompile was StaticCall()ed
120-
in.readOnly = true
121-
defer func() { in.readOnly = false }()
122-
}
123-
12499
var caller ContractRef = e.self
125100
if options.As[callConfig](opts...).unsafeCallerAddressProxying {
126101
// Note that, in addition to being unsafe, this breaks an EVM
@@ -133,7 +108,7 @@ func (e *environment) callContract(typ CallType, addr common.Address, input []by
133108
}
134109
}
135110

136-
if in.readOnly && value != nil && !value.IsZero() {
111+
if e.ReadOnly() && value != nil && !value.IsZero() {
137112
return nil, ErrWriteProtection
138113
}
139114
if !e.UseGas(gas) {

0 commit comments

Comments
 (0)