impr(evm): Move callstack depth to the interpreter (#6632)

This is a more logical / semantically correct place to keep track of the
call stack depth.
This commit is contained in:
Devon Bear 2023-01-19 18:52:08 -05:00 committed by GitHub
parent f2111b4132
commit b8fcb775e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 34 deletions

View File

@ -77,8 +77,6 @@ type EVM struct {
txContext evmtypes.TxContext
// IntraBlockState gives access to the underlying state
intraBlockState evmtypes.IntraBlockState
// Depth is the current call stack
depth int
// chainConfig contains information about the current chain
chainConfig *chain.Config
@ -150,21 +148,6 @@ func (evm *EVM) Cancelled() bool {
return atomic.LoadInt32(&evm.abort) == 1
}
// Depth returns the current call stack depth
func (evm *EVM) Depth() int {
return evm.depth
}
// IncrementDepth increments the call stack depth
func (evm *EVM) IncrementDepth() {
evm.depth++
}
// DecrementDepth decrements the call stack depth
func (evm *EVM) DecrementDepth() {
evm.depth--
}
// CallGasTemp returns the callGasTemp for the EVM
func (evm *EVM) CallGasTemp() uint64 {
return evm.callGasTemp
@ -181,11 +164,11 @@ func (evm *EVM) Interpreter() Interpreter {
}
func (evm *EVM) call(typ OpCode, caller ContractRef, addr libcommon.Address, input []byte, gas uint64, value *uint256.Int, bailout bool) (ret []byte, leftOverGas uint64, err error) {
if evm.config.NoRecursion && evm.depth > 0 {
if evm.config.NoRecursion && evm.interpreter.Depth() > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
if evm.interpreter.Depth() > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
if typ == CALL || typ == CALLCODE {
@ -213,7 +196,7 @@ func (evm *EVM) call(typ OpCode, caller ContractRef, addr libcommon.Address, inp
v = nil
}
// Calling a non existing account, don't do anything, but ping the tracer
if evm.depth == 0 {
if evm.interpreter.Depth() == 0 {
evm.config.Tracer.CaptureStart(evm, caller.Address(), addr, isPrecompile, false /* create */, input, gas, v, code)
defer func(startGas uint64) { // Lazy evaluation of the parameters
evm.config.Tracer.CaptureEnd(ret, 0, err)
@ -242,7 +225,7 @@ func (evm *EVM) call(typ OpCode, caller ContractRef, addr libcommon.Address, inp
if typ == STATICCALL {
v = nil
}
if evm.depth == 0 {
if evm.interpreter.Depth() == 0 {
evm.config.Tracer.CaptureStart(evm, caller.Address(), addr, isPrecompile, false /* create */, input, gas, v, code)
defer func(startGas uint64) { // Lazy evaluation of the parameters
evm.config.Tracer.CaptureEnd(ret, startGas-gas, err)
@ -355,7 +338,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
var err error
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
if evm.interpreter.Depth() > int(params.CallCreateDepth) {
return nil, libcommon.Address{}, gas, ErrDepth
}
if !evm.context.CanTransfer(evm.intraBlockState, caller.Address(), value) {
@ -393,14 +376,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contract.SetCodeOptionalHash(&address, codeAndHash)
if evm.config.Debug {
if evm.depth == 0 {
if evm.interpreter.Depth() == 0 {
evm.config.Tracer.CaptureStart(evm, caller.Address(), address, false /* precompile */, true /* create */, codeAndHash.code, gas, value, nil)
} else {
evm.config.Tracer.CaptureEnter(typ, caller.Address(), address, false /* precompile */, true /* create */, codeAndHash.code, gas, value, nil)
}
}
if evm.config.NoRecursion && evm.depth > 0 {
if evm.config.NoRecursion && evm.interpreter.Depth() > 0 {
return nil, address, gas, nil
}
@ -439,7 +422,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
if evm.config.Debug {
if evm.depth == 0 {
if evm.interpreter.Depth() == 0 {
evm.config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
} else {
evm.config.Tracer.CaptureExit(ret, gas-contract.Gas, err)

View File

@ -57,9 +57,6 @@ type VMInterface interface {
type VMInterpreter interface {
VMInterface
Cancelled() bool
IncrementDepth()
DecrementDepth()
Depth() int
SetCallGasTemp(gas uint64)
CallGasTemp() uint64
StaticCall(caller ContractRef, addr libcommon.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error)

View File

@ -60,6 +60,9 @@ type Interpreter interface {
// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
Run(contract *Contract, input []byte, static bool) ([]byte, error)
// `Depth` returns the current call stack's depth.
Depth() int
}
// ScopeContext contains the things that are per-call, such as stack and memory,
@ -81,7 +84,8 @@ type keccakState interface {
// EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct {
*VM
jt *JumpTable // EVM instruction table
jt *JumpTable // EVM instruction table
depth int
}
// structcheck doesn't see embedding
@ -164,8 +168,8 @@ func NewEVMInterpreter(evm VMInterpreter, cfg Config) *EVMInterpreter {
// ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
// Increment the call depth which is restricted to 1024
in.evm.IncrementDepth()
defer func() { in.evm.DecrementDepth() }()
in.depth++
defer func() { in.depth-- }()
// Make sure the readOnly is only set if we aren't in readOnly yet.
// This makes also sure that the readOnly flag isn't removed for child calls.
@ -212,9 +216,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
defer func() {
if err != nil {
if !logged {
in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.Depth(), err) //nolint:errcheck
in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.depth, err) //nolint:errcheck
} else {
in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.Depth(), err)
in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.depth, err)
}
}
}()
@ -278,7 +282,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
}
if in.cfg.Debug {
in.cfg.Tracer.CaptureState(_pc, op, gasCopy, cost, callContext, in.returnData, in.evm.Depth(), err) //nolint:errcheck
in.cfg.Tracer.CaptureState(_pc, op, gasCopy, cost, callContext, in.returnData, in.depth, err) //nolint:errcheck
logged = true
}
// execute the operation
@ -297,6 +301,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
return res, err
}
// Depth returns the current call stack depth.
func (in *EVMInterpreter) Depth() int {
return in.depth
}
func (vm *VM) setReadonly(outerReadonly bool) func() {
if outerReadonly && !vm.readOnly {
vm.readOnly = true

View File

@ -62,6 +62,10 @@ func (evm *testVM) Run(_ *Contract, _ []byte, readOnly bool) (ret []byte, err er
return
}
func (evm *testVM) Depth() int {
return 0
}
type readOnlyState struct {
outer bool
before bool