go-ethereum/core/vm/jump_table.go
Jeffrey Wilcke 1e5725f03b core/vm: removed err checks from EVM and added snapshot to interpreter
The REVERT op codes reverts the state by returning an error from the
instruction. The instruction was checked during in the EVM. This error
would then determine whether any gas should be consumed or not. If the
error was an errRevert, no gas was further reduced and only state was
reverted.

This solution is error prone as you'll need an err check in multiple
locations and removes the no-err check requirement of the evm. This has
been solved by passing the snapshot to the interpreter and letting the
interpreter do the state reverting if the operation so requires.
2017-04-06 09:01:50 +02:00

878 lines
22 KiB
Go

// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/params"
)
type (
executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
stackValidationFunc func(*Stack) error
memorySizeFunc func(*Stack) *big.Int
)
var errGasUintOverflow = errors.New("gas uint64 overflow")
type operation struct {
// op is the operation function
execute executionFunc
// gasCost is the gas function and returns the gas required for execution
gasCost gasFunc
// validateStack validates the stack (size) for the operation
validateStack stackValidationFunc
// memorySize returns the memory size required for the operation
memorySize memorySizeFunc
// halts indicates whether the operation shoult halt further execution
// and return
halts bool
// jumps indicates whether operation made a jump. This prevents the program
// counter from further incrementing.
jumps bool
// writes determines whether this a state modifying operation
writes bool
// valid is used to check whether the retrieved operation is valid and known
valid bool
// reverts determined whether the operation reverts state
reverts bool
}
var defaultJumpTable = NewJumpTable()
func NewJumpTable() [256]operation {
return [256]operation{
STOP: {
execute: opStop,
gasCost: constGasFunc(0),
validateStack: makeStackFunc(0, 0),
halts: true,
valid: true,
},
ADD: {
execute: opAdd,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
MUL: {
execute: opMul,
gasCost: constGasFunc(GasFastStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
SUB: {
execute: opSub,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
DIV: {
execute: opDiv,
gasCost: constGasFunc(GasFastStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
SDIV: {
execute: opSdiv,
gasCost: constGasFunc(GasFastStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
MOD: {
execute: opMod,
gasCost: constGasFunc(GasFastStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
SMOD: {
execute: opSmod,
gasCost: constGasFunc(GasFastStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
ADDMOD: {
execute: opAddmod,
gasCost: constGasFunc(GasMidStep),
validateStack: makeStackFunc(3, 1),
valid: true,
},
MULMOD: {
execute: opMulmod,
gasCost: constGasFunc(GasMidStep),
validateStack: makeStackFunc(3, 1),
valid: true,
},
EXP: {
execute: opExp,
gasCost: gasExp,
validateStack: makeStackFunc(2, 1),
valid: true,
},
SIGNEXTEND: {
execute: opSignExtend,
gasCost: constGasFunc(GasFastStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
LT: {
execute: opLt,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
GT: {
execute: opGt,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
SLT: {
execute: opSlt,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
SGT: {
execute: opSgt,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
EQ: {
execute: opEq,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
ISZERO: {
execute: opIszero,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(1, 1),
valid: true,
},
AND: {
execute: opAnd,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
XOR: {
execute: opXor,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
OR: {
execute: opOr,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
NOT: {
execute: opNot,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(1, 1),
valid: true,
},
BYTE: {
execute: opByte,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 1),
valid: true,
},
SHA3: {
execute: opSha3,
gasCost: gasSha3,
validateStack: makeStackFunc(2, 1),
memorySize: memorySha3,
valid: true,
},
ADDRESS: {
execute: opAddress,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
BALANCE: {
execute: opBalance,
gasCost: gasBalance,
validateStack: makeStackFunc(1, 1),
valid: true,
},
ORIGIN: {
execute: opOrigin,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
CALLER: {
execute: opCaller,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
CALLVALUE: {
execute: opCallValue,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
CALLDATALOAD: {
execute: opCalldataLoad,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(1, 1),
valid: true,
},
CALLDATASIZE: {
execute: opCalldataSize,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
CALLDATACOPY: {
execute: opCalldataCopy,
gasCost: gasCalldataCopy,
validateStack: makeStackFunc(3, 0),
memorySize: memoryCalldataCopy,
valid: true,
},
CODESIZE: {
execute: opCodeSize,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
CODECOPY: {
execute: opCodeCopy,
gasCost: gasCodeCopy,
validateStack: makeStackFunc(3, 0),
memorySize: memoryCodeCopy,
valid: true,
},
GASPRICE: {
execute: opGasprice,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
EXTCODESIZE: {
execute: opExtCodeSize,
gasCost: gasExtCodeSize,
validateStack: makeStackFunc(1, 1),
valid: true,
},
EXTCODECOPY: {
execute: opExtCodeCopy,
gasCost: gasExtCodeCopy,
validateStack: makeStackFunc(4, 0),
memorySize: memoryExtCodeCopy,
valid: true,
},
BLOCKHASH: {
execute: opBlockhash,
gasCost: constGasFunc(GasExtStep),
validateStack: makeStackFunc(1, 1),
valid: true,
},
COINBASE: {
execute: opCoinbase,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
TIMESTAMP: {
execute: opTimestamp,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
NUMBER: {
execute: opNumber,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
DIFFICULTY: {
execute: opDifficulty,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
GASLIMIT: {
execute: opGasLimit,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
POP: {
execute: opPop,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(1, 0),
valid: true,
},
MLOAD: {
execute: opMload,
gasCost: gasMLoad,
validateStack: makeStackFunc(1, 1),
memorySize: memoryMLoad,
valid: true,
},
MSTORE: {
execute: opMstore,
gasCost: gasMStore,
validateStack: makeStackFunc(2, 0),
memorySize: memoryMStore,
valid: true,
},
MSTORE8: {
execute: opMstore8,
gasCost: gasMStore8,
memorySize: memoryMStore8,
validateStack: makeStackFunc(2, 0),
valid: true,
},
SLOAD: {
execute: opSload,
gasCost: gasSLoad,
validateStack: makeStackFunc(1, 1),
valid: true,
},
SSTORE: {
execute: opSstore,
gasCost: gasSStore,
validateStack: makeStackFunc(2, 0),
valid: true,
writes: true,
},
JUMP: {
execute: opJump,
gasCost: constGasFunc(GasMidStep),
validateStack: makeStackFunc(1, 0),
jumps: true,
valid: true,
},
JUMPI: {
execute: opJumpi,
gasCost: constGasFunc(GasSlowStep),
validateStack: makeStackFunc(2, 0),
jumps: true,
valid: true,
},
PC: {
execute: opPc,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
MSIZE: {
execute: opMsize,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
GAS: {
execute: opGas,
gasCost: constGasFunc(GasQuickStep),
validateStack: makeStackFunc(0, 1),
valid: true,
},
// TODO:
// * Determine cost
REVERT: {
execute: opRevert,
gasCost: constGasFunc(GasFastestStep),
validateStack: makeStackFunc(2, 0),
valid: true,
reverts: true,
},
JUMPDEST: {
execute: opJumpdest,
gasCost: constGasFunc(params.JumpdestGas),
validateStack: makeStackFunc(0, 0),
valid: true,
},
PUSH1: {
execute: makePush(1, big.NewInt(1)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH2: {
execute: makePush(2, big.NewInt(2)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH3: {
execute: makePush(3, big.NewInt(3)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH4: {
execute: makePush(4, big.NewInt(4)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH5: {
execute: makePush(5, big.NewInt(5)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH6: {
execute: makePush(6, big.NewInt(6)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH7: {
execute: makePush(7, big.NewInt(7)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH8: {
execute: makePush(8, big.NewInt(8)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH9: {
execute: makePush(9, big.NewInt(9)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH10: {
execute: makePush(10, big.NewInt(10)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH11: {
execute: makePush(11, big.NewInt(11)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH12: {
execute: makePush(12, big.NewInt(12)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH13: {
execute: makePush(13, big.NewInt(13)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH14: {
execute: makePush(14, big.NewInt(14)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH15: {
execute: makePush(15, big.NewInt(15)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH16: {
execute: makePush(16, big.NewInt(16)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH17: {
execute: makePush(17, big.NewInt(17)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH18: {
execute: makePush(18, big.NewInt(18)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH19: {
execute: makePush(19, big.NewInt(19)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH20: {
execute: makePush(20, big.NewInt(20)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH21: {
execute: makePush(21, big.NewInt(21)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH22: {
execute: makePush(22, big.NewInt(22)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH23: {
execute: makePush(23, big.NewInt(23)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH24: {
execute: makePush(24, big.NewInt(24)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH25: {
execute: makePush(25, big.NewInt(25)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH26: {
execute: makePush(26, big.NewInt(26)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH27: {
execute: makePush(27, big.NewInt(27)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH28: {
execute: makePush(28, big.NewInt(28)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH29: {
execute: makePush(29, big.NewInt(29)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH30: {
execute: makePush(30, big.NewInt(30)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH31: {
execute: makePush(31, big.NewInt(31)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH32: {
execute: makePush(32, big.NewInt(32)),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
DUP1: {
execute: makeDup(1),
gasCost: gasDup,
validateStack: makeDupStackFunc(1),
valid: true,
},
DUP2: {
execute: makeDup(2),
gasCost: gasDup,
validateStack: makeDupStackFunc(2),
valid: true,
},
DUP3: {
execute: makeDup(3),
gasCost: gasDup,
validateStack: makeDupStackFunc(3),
valid: true,
},
DUP4: {
execute: makeDup(4),
gasCost: gasDup,
validateStack: makeDupStackFunc(4),
valid: true,
},
DUP5: {
execute: makeDup(5),
gasCost: gasDup,
validateStack: makeDupStackFunc(5),
valid: true,
},
DUP6: {
execute: makeDup(6),
gasCost: gasDup,
validateStack: makeDupStackFunc(6),
valid: true,
},
DUP7: {
execute: makeDup(7),
gasCost: gasDup,
validateStack: makeDupStackFunc(7),
valid: true,
},
DUP8: {
execute: makeDup(8),
gasCost: gasDup,
validateStack: makeDupStackFunc(8),
valid: true,
},
DUP9: {
execute: makeDup(9),
gasCost: gasDup,
validateStack: makeDupStackFunc(9),
valid: true,
},
DUP10: {
execute: makeDup(10),
gasCost: gasDup,
validateStack: makeDupStackFunc(10),
valid: true,
},
DUP11: {
execute: makeDup(11),
gasCost: gasDup,
validateStack: makeDupStackFunc(11),
valid: true,
},
DUP12: {
execute: makeDup(12),
gasCost: gasDup,
validateStack: makeDupStackFunc(12),
valid: true,
},
DUP13: {
execute: makeDup(13),
gasCost: gasDup,
validateStack: makeDupStackFunc(13),
valid: true,
},
DUP14: {
execute: makeDup(14),
gasCost: gasDup,
validateStack: makeDupStackFunc(14),
valid: true,
},
DUP15: {
execute: makeDup(15),
gasCost: gasDup,
validateStack: makeDupStackFunc(15),
valid: true,
},
DUP16: {
execute: makeDup(16),
gasCost: gasDup,
validateStack: makeDupStackFunc(16),
valid: true,
},
SWAP1: {
execute: makeSwap(1),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(2),
valid: true,
},
SWAP2: {
execute: makeSwap(2),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(3),
valid: true,
},
SWAP3: {
execute: makeSwap(3),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(4),
valid: true,
},
SWAP4: {
execute: makeSwap(4),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(5),
valid: true,
},
SWAP5: {
execute: makeSwap(5),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(6),
valid: true,
},
SWAP6: {
execute: makeSwap(6),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(7),
valid: true,
},
SWAP7: {
execute: makeSwap(7),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(8),
valid: true,
},
SWAP8: {
execute: makeSwap(8),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(9),
valid: true,
},
SWAP9: {
execute: makeSwap(9),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(10),
valid: true,
},
SWAP10: {
execute: makeSwap(10),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(11),
valid: true,
},
SWAP11: {
execute: makeSwap(11),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(12),
valid: true,
},
SWAP12: {
execute: makeSwap(12),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(13),
valid: true,
},
SWAP13: {
execute: makeSwap(13),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(14),
valid: true,
},
SWAP14: {
execute: makeSwap(14),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(15),
valid: true,
},
SWAP15: {
execute: makeSwap(15),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(16),
valid: true,
},
SWAP16: {
execute: makeSwap(16),
gasCost: gasSwap,
validateStack: makeSwapStackFunc(17),
valid: true,
},
LOG0: {
execute: makeLog(0),
gasCost: makeGasLog(0),
validateStack: makeStackFunc(2, 0),
memorySize: memoryLog,
valid: true,
},
LOG1: {
execute: makeLog(1),
gasCost: makeGasLog(1),
validateStack: makeStackFunc(3, 0),
memorySize: memoryLog,
valid: true,
},
LOG2: {
execute: makeLog(2),
gasCost: makeGasLog(2),
validateStack: makeStackFunc(4, 0),
memorySize: memoryLog,
valid: true,
},
LOG3: {
execute: makeLog(3),
gasCost: makeGasLog(3),
validateStack: makeStackFunc(5, 0),
memorySize: memoryLog,
valid: true,
},
LOG4: {
execute: makeLog(4),
gasCost: makeGasLog(4),
validateStack: makeStackFunc(6, 0),
memorySize: memoryLog,
valid: true,
},
CREATE: {
execute: opCreate,
gasCost: gasCreate,
validateStack: makeStackFunc(3, 1),
memorySize: memoryCreate,
valid: true,
writes: true,
},
CALL: {
execute: opCall,
gasCost: gasCall,
validateStack: makeStackFunc(7, 1),
memorySize: memoryCall,
valid: true,
},
CALLCODE: {
execute: opCallCode,
gasCost: gasCallCode,
validateStack: makeStackFunc(7, 1),
memorySize: memoryCall,
valid: true,
},
RETURN: {
execute: opReturn,
gasCost: gasReturn,
validateStack: makeStackFunc(2, 0),
memorySize: memoryReturn,
halts: true,
valid: true,
},
DELEGATECALL: {
execute: opDelegateCall,
gasCost: gasDelegateCall,
validateStack: makeStackFunc(6, 1),
memorySize: memoryDelegateCall,
valid: true,
},
SELFDESTRUCT: {
execute: opSuicide,
gasCost: gasSuicide,
validateStack: makeStackFunc(1, 0),
halts: true,
valid: true,
writes: true,
},
}
}