Skip to content

Commit

Permalink
core/vm: simplify error handling in interpreter loop (#23952)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
chfast authored Nov 29, 2021
1 parent 86fe359 commit 1fa9172
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 41 deletions.
4 changes: 4 additions & 0 deletions core/vm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ var (
ErrGasUintOverflow = errors.New("gas uint64 overflow")
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
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
Expand Down
23 changes: 15 additions & 8 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
if !scope.Contract.validJumpdest(&pos) {
return nil, ErrInvalidJump
}
*pc = pos.Uint64()
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
return nil, nil
}

Expand All @@ -536,9 +536,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
if !scope.Contract.validJumpdest(&pos) {
return nil, ErrInvalidJump
}
*pc = pos.Uint64()
} else {
*pc++
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
}
return nil, nil
}
Expand Down Expand Up @@ -598,8 +596,10 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
scope.Contract.Gas += returnGas

if suberr == ErrExecutionReverted {
interpreter.returnData = res // set REVERT data to return data buffer
return res, nil
}
interpreter.returnData = nil // clear dirty return data buffer
return nil, nil
}

Expand Down Expand Up @@ -634,8 +634,10 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
scope.Contract.Gas += returnGas

if suberr == ErrExecutionReverted {
interpreter.returnData = res // set REVERT data to return data buffer
return res, nil
}
interpreter.returnData = nil // clear dirty return data buffer
return nil, nil
}

Expand Down Expand Up @@ -674,6 +676,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
}
scope.Contract.Gas += returnGas

interpreter.returnData = ret
return ret, nil
}

Expand Down Expand Up @@ -709,6 +712,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
}
scope.Contract.Gas += returnGas

interpreter.returnData = ret
return ret, nil
}

Expand Down Expand Up @@ -737,6 +741,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
}
scope.Contract.Gas += returnGas

interpreter.returnData = ret
return ret, nil
}

Expand Down Expand Up @@ -765,25 +770,27 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
}
scope.Contract.Gas += returnGas

interpreter.returnData = ret
return ret, nil
}

func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
offset, size := scope.Stack.pop(), scope.Stack.pop()
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) {
offset, size := scope.Stack.pop(), scope.Stack.pop()
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) {
return nil, nil
return nil, errStopToken
}

func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
Expand All @@ -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.CaptureExit([]byte{}, 0, nil)
}
return nil, nil
return nil, errStopToken
}

// following functions are used by the instruction jump table
Expand Down
24 changes: 9 additions & 15 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,22 +259,16 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (

// execute the operation
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 {
case err != nil:
return nil, err
case operation.reverts:
return res, ErrExecutionReverted
case operation.halts:
return res, nil
case !operation.jumps:
pc++
if err != nil {
break
}
pc++
}
return nil, nil

if err == errStopToken {
err = nil // clear stop token error
}

return res, err
}
19 changes: 1 addition & 18 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@ type operation struct {
// memorySize returns the memory size required for the operation
memorySize memorySizeFunc

halts bool // indicates whether the operation should halt further execution
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
writes bool // determines whether this a state modifying operation
}

var (
Expand Down Expand Up @@ -128,7 +124,6 @@ func newConstantinopleInstructionSet() JumpTable {
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
writes: true,
returns: true,
}
return instructionSet
}
Expand All @@ -144,7 +139,6 @@ func newByzantiumInstructionSet() JumpTable {
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryStaticCall,
returns: true,
}
instructionSet[RETURNDATASIZE] = &operation{
execute: opReturnDataSize,
Expand All @@ -166,8 +160,6 @@ func newByzantiumInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
reverts: true,
returns: true,
}
return instructionSet
}
Expand Down Expand Up @@ -204,7 +196,6 @@ func newHomesteadInstructionSet() JumpTable {
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
returns: true,
}
return instructionSet
}
Expand All @@ -218,7 +209,6 @@ func newFrontierInstructionSet() JumpTable {
constantGas: 0,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
halts: true,
},
ADD: {
execute: opAdd,
Expand Down Expand Up @@ -528,14 +518,12 @@ func newFrontierInstructionSet() JumpTable {
constantGas: GasMidStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
jumps: true,
},
JUMPI: {
execute: opJumpi,
constantGas: GasSlowStep,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
jumps: true,
},
PC: {
execute: opPc,
Expand Down Expand Up @@ -993,7 +981,6 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(3, 1),
memorySize: memoryCreate,
writes: true,
returns: true,
},
CALL: {
execute: opCall,
Expand All @@ -1002,7 +989,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
returns: true,
},
CALLCODE: {
execute: opCallCode,
Expand All @@ -1011,22 +997,19 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
returns: true,
},
RETURN: {
execute: opReturn,
dynamicGas: gasReturn,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryReturn,
halts: true,
},
SELFDESTRUCT: {
execute: opSuicide,
dynamicGas: gasSelfdestruct,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
halts: true,
writes: true,
},
}
Expand Down

0 comments on commit 1fa9172

Please sign in to comment.