以太坊指令集与操作码解释器
EVM事实是个堆栈机器。指令可能会使用栈上的数值作为参数,也会将值作为结果压入栈中,而指令的构成是由我们所编写的合约的ABI文件所生产,大致结构为
编写合约 > 生成ABI > 解析ABI得出指令集 > eth会将指令通过core/vm/opcodes.go文件中的操作码映射,映射成操作码集 > 生成一个operation[256] >
type operation struct {
//下列函数在core/vm/jump_table.go文件中定义了模板
//操作函数
execute executionFunc
// gas函数,返回执行所需的gas
gasCost gasFunc
// 验证操作的堆栈(大小)
validateStack stackValidationFunc
// 返回操作所需的内存大小
memorySize memorySizeFunc
halts bool // 指示操作是否应停止进一步执行
jumps bool // 指示程序计数器是否不应递增
writes bool // 确定此操作是否为状态修改操作
valid bool // 指示检索的操作是否有效和已知
reverts bool // 确定操作是否恢复状态(隐式停止)
returns bool // 确定操作是否设置返回的数据内容
}
type (
executionFunc func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error)
stackValidationFunc func(*Stack) error
memorySizeFunc func(*Stack) *big.Int
)
根据当前的版本选择对应的操作码生成函数
var (
frontierInstructionSet = newFrontierInstructionSet()
homesteadInstructionSet = newHomesteadInstructionSet()
byzantiumInstructionSet = newByzantiumInstructionSet()
constantinopleInstructionSet = newConstantinopleInstructionSet()
)
例如:家园版本
//家园阶段可以执行的指令。
func newHomesteadInstructionSet() [256]operation {
instructionSet := newFrontierInstructionSet()
instructionSet[DELEGATECALL] = operation{
execute: opDelegateCall,
gasCost: gasDelegateCall,
validateStack: makeStackFunc(6, 1),
memorySize: memoryDelegateCall,
valid: true,
returns: true,
}
return instructionSet
}
这样,对evm的操作实际上是操作码对程序的操作。
core/vm/opcodes.go:部分代码,实际上就是一堆的常量操作符,这些操作符会在core/vm/jump_table.go这个文件中映射成对应的操作函数,用于执行
const (
STOP OpCode = iota
ADD
MUL
SUB
DIV
SDIV
MOD
SMOD
ADDMOD
MULMOD
EXP
SIGNEXTEND
)
// 0x10 range - comparison ops.
const (
LT OpCode = iota + 0x10
GT
SLT
SGT
EQ
ISZERO
AND
OR
XOR
NOT
BYTE
SHL
SHR
SAR
SHA3 = 0x20
)
// 0x30 range - closure state.
const (
ADDRESS OpCode = 0x30 + iota
BALANCE
ORIGIN
CALLER
CALLVALUE
CALLDATALOAD
CALLDATASIZE
CALLDATACOPY
CODESIZE
CODECOPY
GASPRICE
EXTCODESIZE
EXTCODECOPY
RETURNDATASIZE
RETURNDATACOPY
EXTCODEHASH
)
// 0x40 range - block operations.
const (
BLOCKHASH OpCode = 0x40 + iota
COINBASE
TIMESTAMP
NUMBER
DIFFICULTY
GASLIMIT
)
// 0x50 range - 'storage' and execution.
const (
POP OpCode = 0x50 + iota
MLOAD
MSTORE
MSTORE8
SLOAD
SSTORE
JUMP
JUMPI
PC
MSIZE
GAS
JUMPDEST
)
// 0x60 range.
const (
PUSH1 OpCode = 0x60 + iota
PUSH2
PUSH3
PUSH4
PUSH5
PUSH6
PUSH7
PUSH8
PUSH9
PUSH10
PUSH11
PUSH12
PUSH13
PUSH14
PUSH15
PUSH16
PUSH17
PUSH18
PUSH19
PUSH20
PUSH21
PUSH22
PUSH23
PUSH24
PUSH25
PUSH26
PUSH27
PUSH28
PUSH29
PUSH30
PUSH31
PUSH32
DUP1
DUP2
DUP3
DUP4
DUP5
DUP6
DUP7
DUP8
DUP9
DUP10
DUP11
DUP12
DUP13
DUP14
DUP15
DUP16
SWAP1
SWAP2
SWAP3
SWAP4
SWAP5
SWAP6
SWAP7
SWAP8
SWAP9
SWAP10
SWAP11
SWAP12
SWAP13
SWAP14
SWAP15
SWAP16
)
// 0xa0 range - logging ops.
const (
LOG0 OpCode = 0xa0 + iota
LOG1
LOG2
LOG3
LOG4
)
// unofficial opcodes used for parsing.
const (
PUSH OpCode = 0xb0 + iota
DUP
SWAP
)
core/vm/jump_table.go:都是对操作码的映射解释,可以将jump理解为操作码解释器,将opcodes理解为指令解释器
func newFrontierInstructionSet() [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