mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-20 13:44:31 +00:00
refactor: set EVM.readOnly and depth before running stateful precompile (#210)
## Why this should be merged Improved clarity. ## How this works Semantically equivalent refactor. These values aren't accessible / needed during execution of a stateful precompile, but are important when making outgoing calls so were originally only set in the `PrecompileEnvironment.Call()` implementation. This was confusing and led to a false-positive bug report. ## How this was tested Existing tests.
This commit is contained in:
parent
e7035f19ee
commit
62dc2ea087
2 changed files with 28 additions and 34 deletions
|
|
@ -101,6 +101,12 @@ func (t CallType) isValid() bool {
|
|||
}
|
||||
}
|
||||
|
||||
// readOnly returns whether the CallType induces a read-only state if not
|
||||
// already in one.
|
||||
func (t CallType) readOnly() bool {
|
||||
return t == StaticCall
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of the CallType.
|
||||
func (t CallType) String() string {
|
||||
if t.isValid() {
|
||||
|
|
@ -120,15 +126,28 @@ func (t CallType) OpCode() OpCode {
|
|||
// run runs the [PrecompiledContract], differentiating between stateful and
|
||||
// regular types, updating `args.gasRemaining` in the stateful case.
|
||||
func (args *evmCallArgs) run(p PrecompiledContract, input []byte) (ret []byte, err error) {
|
||||
switch p := p.(type) {
|
||||
default:
|
||||
sp, ok := p.(statefulPrecompile)
|
||||
if !ok {
|
||||
return p.Run(input)
|
||||
case statefulPrecompile:
|
||||
env := args.env()
|
||||
ret, err := p(env, input)
|
||||
args.gasRemaining = env.Gas()
|
||||
return ret, err
|
||||
}
|
||||
|
||||
env := args.env()
|
||||
// Depth and read-only setting are handled by [EVMInterpreter.Run],
|
||||
// which isn't used for precompiles, so we need to do it ourselves to
|
||||
// maintain the expected invariants.
|
||||
in := env.evm.interpreter
|
||||
|
||||
in.evm.depth++
|
||||
defer func() { in.evm.depth-- }()
|
||||
|
||||
if env.callType.readOnly() && !in.readOnly {
|
||||
in.readOnly = true
|
||||
defer func() { in.readOnly = false }()
|
||||
}
|
||||
|
||||
ret, err = sp(env, input)
|
||||
args.gasRemaining = env.Gas()
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// PrecompiledStatefulContract is the stateful equivalent of a
|
||||
|
|
|
|||
|
|
@ -63,19 +63,7 @@ func (e *environment) refundGas(add uint64) error {
|
|||
}
|
||||
|
||||
func (e *environment) ReadOnly() bool {
|
||||
// A switch statement provides clearer code coverage for difficult-to-test
|
||||
// cases.
|
||||
switch {
|
||||
case e.callType == StaticCall:
|
||||
// evm.interpreter.readOnly is only set to true via a call to
|
||||
// EVMInterpreter.Run() so, if a precompile is called directly with
|
||||
// StaticCall(), then readOnly might not be set yet.
|
||||
return true
|
||||
case e.evm.interpreter.readOnly:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return e.evm.interpreter.readOnly
|
||||
}
|
||||
|
||||
func (e *environment) Addresses() *libevm.AddressContext {
|
||||
|
|
@ -116,19 +104,6 @@ func (e *environment) Call(addr common.Address, input []byte, gas uint64, value
|
|||
}
|
||||
|
||||
func (e *environment) callContract(typ CallType, addr common.Address, input []byte, gas uint64, value *uint256.Int, opts ...CallOption) (retData []byte, retErr error) {
|
||||
// Depth and read-only setting are handled by [EVMInterpreter.Run], which
|
||||
// isn't used for precompiles, so we need to do it ourselves to maintain the
|
||||
// expected invariants.
|
||||
in := e.evm.interpreter
|
||||
|
||||
in.evm.depth++
|
||||
defer func() { in.evm.depth-- }()
|
||||
|
||||
if e.ReadOnly() && !in.readOnly { // i.e. the precompile was StaticCall()ed
|
||||
in.readOnly = true
|
||||
defer func() { in.readOnly = false }()
|
||||
}
|
||||
|
||||
var caller ContractRef = e.self
|
||||
if options.As[callConfig](opts...).unsafeCallerAddressProxying {
|
||||
// Note that, in addition to being unsafe, this breaks an EVM
|
||||
|
|
@ -141,7 +116,7 @@ func (e *environment) callContract(typ CallType, addr common.Address, input []by
|
|||
}
|
||||
}
|
||||
|
||||
if in.readOnly && value != nil && !value.IsZero() {
|
||||
if e.ReadOnly() && value != nil && !value.IsZero() {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
if !e.UseGas(gas) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue