core/vm: simplify error handling in interpreter loop (#23952)
* core/vm: break loop on any error * core/vm: move ErrExecutionReverted to opRevert() * core/vm: use "stop token" to stop the loop * core/vm: unconditionally pc++ in the loop * core/vm: set return data in instruction impls
This commit is contained in:
parent
86fe359a56
commit
1fa91729f2
@ -36,6 +36,10 @@ var (
|
|||||||
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||||
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
|
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
|
||||||
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
|
||||||
|
|
||||||
|
// errStopToken is an internal token indicating interpreter loop termination,
|
||||||
|
// never returned to outside callers.
|
||||||
|
errStopToken = errors.New("stop token")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrStackUnderflow wraps an evm error when the items on the stack less
|
// ErrStackUnderflow wraps an evm error when the items on the stack less
|
||||||
|
@ -526,7 +526,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
|||||||
if !scope.Contract.validJumpdest(&pos) {
|
if !scope.Contract.validJumpdest(&pos) {
|
||||||
return nil, ErrInvalidJump
|
return nil, ErrInvalidJump
|
||||||
}
|
}
|
||||||
*pc = pos.Uint64()
|
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,9 +536,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
|||||||
if !scope.Contract.validJumpdest(&pos) {
|
if !scope.Contract.validJumpdest(&pos) {
|
||||||
return nil, ErrInvalidJump
|
return nil, ErrInvalidJump
|
||||||
}
|
}
|
||||||
*pc = pos.Uint64()
|
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
|
||||||
} else {
|
|
||||||
*pc++
|
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -598,8 +596,10 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
|||||||
scope.Contract.Gas += returnGas
|
scope.Contract.Gas += returnGas
|
||||||
|
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
|
interpreter.returnData = res // set REVERT data to return data buffer
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
interpreter.returnData = nil // clear dirty return data buffer
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,8 +634,10 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
|||||||
scope.Contract.Gas += returnGas
|
scope.Contract.Gas += returnGas
|
||||||
|
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
|
interpreter.returnData = res // set REVERT data to return data buffer
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
interpreter.returnData = nil // clear dirty return data buffer
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,6 +676,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
|||||||
}
|
}
|
||||||
scope.Contract.Gas += returnGas
|
scope.Contract.Gas += returnGas
|
||||||
|
|
||||||
|
interpreter.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,6 +712,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
|||||||
}
|
}
|
||||||
scope.Contract.Gas += returnGas
|
scope.Contract.Gas += returnGas
|
||||||
|
|
||||||
|
interpreter.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,6 +741,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
|||||||
}
|
}
|
||||||
scope.Contract.Gas += returnGas
|
scope.Contract.Gas += returnGas
|
||||||
|
|
||||||
|
interpreter.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -765,6 +770,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||||||
}
|
}
|
||||||
scope.Contract.Gas += returnGas
|
scope.Contract.Gas += returnGas
|
||||||
|
|
||||||
|
interpreter.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,18 +778,19 @@ func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
|||||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
|
|
||||||
return ret, nil
|
return ret, errStopToken
|
||||||
}
|
}
|
||||||
|
|
||||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
|
|
||||||
return ret, nil
|
interpreter.returnData = ret
|
||||||
|
return ret, ErrExecutionReverted
|
||||||
}
|
}
|
||||||
|
|
||||||
func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
return nil, nil
|
return nil, errStopToken
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
@ -795,7 +802,7 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
|||||||
interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||||
interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
|
interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, errStopToken
|
||||||
}
|
}
|
||||||
|
|
||||||
// following functions are used by the instruction jump table
|
// following functions are used by the instruction jump table
|
||||||
|
@ -259,22 +259,16 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
|
|
||||||
// execute the operation
|
// execute the operation
|
||||||
res, err = operation.execute(&pc, in, callContext)
|
res, err = operation.execute(&pc, in, callContext)
|
||||||
// if the operation clears the return data (e.g. it has returning data)
|
|
||||||
// set the last return to the result of the operation.
|
|
||||||
if operation.returns {
|
|
||||||
in.returnData = res
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
if err != nil {
|
||||||
case err != nil:
|
break
|
||||||
return nil, err
|
|
||||||
case operation.reverts:
|
|
||||||
return res, ErrExecutionReverted
|
|
||||||
case operation.halts:
|
|
||||||
return res, nil
|
|
||||||
case !operation.jumps:
|
|
||||||
pc++
|
|
||||||
}
|
}
|
||||||
|
pc++
|
||||||
}
|
}
|
||||||
return nil, nil
|
|
||||||
|
if err == errStopToken {
|
||||||
|
err = nil // clear stop token error
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,7 @@ type operation struct {
|
|||||||
// memorySize returns the memory size required for the operation
|
// memorySize returns the memory size required for the operation
|
||||||
memorySize memorySizeFunc
|
memorySize memorySizeFunc
|
||||||
|
|
||||||
halts bool // indicates whether the operation should halt further execution
|
writes bool // determines whether this a state modifying operation
|
||||||
jumps bool // indicates whether the program counter should not increment
|
|
||||||
writes bool // determines whether this a state modifying operation
|
|
||||||
reverts bool // determines whether the operation reverts state (implicitly halts)
|
|
||||||
returns bool // determines whether the operations sets the return data content
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -128,7 +124,6 @@ func newConstantinopleInstructionSet() JumpTable {
|
|||||||
maxStack: maxStack(4, 1),
|
maxStack: maxStack(4, 1),
|
||||||
memorySize: memoryCreate2,
|
memorySize: memoryCreate2,
|
||||||
writes: true,
|
writes: true,
|
||||||
returns: true,
|
|
||||||
}
|
}
|
||||||
return instructionSet
|
return instructionSet
|
||||||
}
|
}
|
||||||
@ -144,7 +139,6 @@ func newByzantiumInstructionSet() JumpTable {
|
|||||||
minStack: minStack(6, 1),
|
minStack: minStack(6, 1),
|
||||||
maxStack: maxStack(6, 1),
|
maxStack: maxStack(6, 1),
|
||||||
memorySize: memoryStaticCall,
|
memorySize: memoryStaticCall,
|
||||||
returns: true,
|
|
||||||
}
|
}
|
||||||
instructionSet[RETURNDATASIZE] = &operation{
|
instructionSet[RETURNDATASIZE] = &operation{
|
||||||
execute: opReturnDataSize,
|
execute: opReturnDataSize,
|
||||||
@ -166,8 +160,6 @@ func newByzantiumInstructionSet() JumpTable {
|
|||||||
minStack: minStack(2, 0),
|
minStack: minStack(2, 0),
|
||||||
maxStack: maxStack(2, 0),
|
maxStack: maxStack(2, 0),
|
||||||
memorySize: memoryRevert,
|
memorySize: memoryRevert,
|
||||||
reverts: true,
|
|
||||||
returns: true,
|
|
||||||
}
|
}
|
||||||
return instructionSet
|
return instructionSet
|
||||||
}
|
}
|
||||||
@ -204,7 +196,6 @@ func newHomesteadInstructionSet() JumpTable {
|
|||||||
minStack: minStack(6, 1),
|
minStack: minStack(6, 1),
|
||||||
maxStack: maxStack(6, 1),
|
maxStack: maxStack(6, 1),
|
||||||
memorySize: memoryDelegateCall,
|
memorySize: memoryDelegateCall,
|
||||||
returns: true,
|
|
||||||
}
|
}
|
||||||
return instructionSet
|
return instructionSet
|
||||||
}
|
}
|
||||||
@ -218,7 +209,6 @@ func newFrontierInstructionSet() JumpTable {
|
|||||||
constantGas: 0,
|
constantGas: 0,
|
||||||
minStack: minStack(0, 0),
|
minStack: minStack(0, 0),
|
||||||
maxStack: maxStack(0, 0),
|
maxStack: maxStack(0, 0),
|
||||||
halts: true,
|
|
||||||
},
|
},
|
||||||
ADD: {
|
ADD: {
|
||||||
execute: opAdd,
|
execute: opAdd,
|
||||||
@ -528,14 +518,12 @@ func newFrontierInstructionSet() JumpTable {
|
|||||||
constantGas: GasMidStep,
|
constantGas: GasMidStep,
|
||||||
minStack: minStack(1, 0),
|
minStack: minStack(1, 0),
|
||||||
maxStack: maxStack(1, 0),
|
maxStack: maxStack(1, 0),
|
||||||
jumps: true,
|
|
||||||
},
|
},
|
||||||
JUMPI: {
|
JUMPI: {
|
||||||
execute: opJumpi,
|
execute: opJumpi,
|
||||||
constantGas: GasSlowStep,
|
constantGas: GasSlowStep,
|
||||||
minStack: minStack(2, 0),
|
minStack: minStack(2, 0),
|
||||||
maxStack: maxStack(2, 0),
|
maxStack: maxStack(2, 0),
|
||||||
jumps: true,
|
|
||||||
},
|
},
|
||||||
PC: {
|
PC: {
|
||||||
execute: opPc,
|
execute: opPc,
|
||||||
@ -993,7 +981,6 @@ func newFrontierInstructionSet() JumpTable {
|
|||||||
maxStack: maxStack(3, 1),
|
maxStack: maxStack(3, 1),
|
||||||
memorySize: memoryCreate,
|
memorySize: memoryCreate,
|
||||||
writes: true,
|
writes: true,
|
||||||
returns: true,
|
|
||||||
},
|
},
|
||||||
CALL: {
|
CALL: {
|
||||||
execute: opCall,
|
execute: opCall,
|
||||||
@ -1002,7 +989,6 @@ func newFrontierInstructionSet() JumpTable {
|
|||||||
minStack: minStack(7, 1),
|
minStack: minStack(7, 1),
|
||||||
maxStack: maxStack(7, 1),
|
maxStack: maxStack(7, 1),
|
||||||
memorySize: memoryCall,
|
memorySize: memoryCall,
|
||||||
returns: true,
|
|
||||||
},
|
},
|
||||||
CALLCODE: {
|
CALLCODE: {
|
||||||
execute: opCallCode,
|
execute: opCallCode,
|
||||||
@ -1011,7 +997,6 @@ func newFrontierInstructionSet() JumpTable {
|
|||||||
minStack: minStack(7, 1),
|
minStack: minStack(7, 1),
|
||||||
maxStack: maxStack(7, 1),
|
maxStack: maxStack(7, 1),
|
||||||
memorySize: memoryCall,
|
memorySize: memoryCall,
|
||||||
returns: true,
|
|
||||||
},
|
},
|
||||||
RETURN: {
|
RETURN: {
|
||||||
execute: opReturn,
|
execute: opReturn,
|
||||||
@ -1019,14 +1004,12 @@ func newFrontierInstructionSet() JumpTable {
|
|||||||
minStack: minStack(2, 0),
|
minStack: minStack(2, 0),
|
||||||
maxStack: maxStack(2, 0),
|
maxStack: maxStack(2, 0),
|
||||||
memorySize: memoryReturn,
|
memorySize: memoryReturn,
|
||||||
halts: true,
|
|
||||||
},
|
},
|
||||||
SELFDESTRUCT: {
|
SELFDESTRUCT: {
|
||||||
execute: opSuicide,
|
execute: opSuicide,
|
||||||
dynamicGas: gasSelfdestruct,
|
dynamicGas: gasSelfdestruct,
|
||||||
minStack: minStack(1, 0),
|
minStack: minStack(1, 0),
|
||||||
maxStack: maxStack(1, 0),
|
maxStack: maxStack(1, 0),
|
||||||
halts: true,
|
|
||||||
writes: true,
|
writes: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user