以太坊源码解读(6)以太坊VM源码解读之指令集与操作码解释器

以太坊指令集与操作码解释器

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, 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,
      },
      JUMPDEST: {
         execute:       opJumpdest,
         gasCost:       constGasFunc(params.JumpdestGas),
         validateStack: makeStackFunc(0, 0),
         valid:         true,
      },
      PUSH1: {
         execute:       makePush(1, 1),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH2: {
         execute:       makePush(2, 2),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH3: {
         execute:       makePush(3, 3),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH4: {
         execute:       makePush(4, 4),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH5: {
         execute:       makePush(5, 5),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH6: {
         execute:       makePush(6, 6),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH7: {
         execute:       makePush(7, 7),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH8: {
         execute:       makePush(8, 8),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH9: {
         execute:       makePush(9, 9),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH10: {
         execute:       makePush(10, 10),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH11: {
         execute:       makePush(11, 11),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH12: {
         execute:       makePush(12, 12),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH13: {
         execute:       makePush(13, 13),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH14: {
         execute:       makePush(14, 14),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH15: {
         execute:       makePush(15, 15),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH16: {
         execute:       makePush(16, 16),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH17: {
         execute:       makePush(17, 17),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH18: {
         execute:       makePush(18, 18),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH19: {
         execute:       makePush(19, 19),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH20: {
         execute:       makePush(20, 20),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH21: {
         execute:       makePush(21, 21),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH22: {
         execute:       makePush(22, 22),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH23: {
         execute:       makePush(23, 23),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH24: {
         execute:       makePush(24, 24),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH25: {
         execute:       makePush(25, 25),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH26: {
         execute:       makePush(26, 26),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH27: {
         execute:       makePush(27, 27),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH28: {
         execute:       makePush(28, 28),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH29: {
         execute:       makePush(29, 29),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH30: {
         execute:       makePush(30, 30),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH31: {
         execute:       makePush(31, 31),
         gasCost:       gasPush,
         validateStack: makeStackFunc(0, 1),
         valid:         true,
      },
      PUSH32: {
         execute:       makePush(32, 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,
         writes:        true,
      },
      LOG1: {
         execute:       makeLog(1),
         gasCost:       makeGasLog(1),
         validateStack: makeStackFunc(3, 0),
         memorySize:    memoryLog,
         valid:         true,
         writes:        true,
      },
      LOG2: {
         execute:       makeLog(2),
         gasCost:       makeGasLog(2),
         validateStack: makeStackFunc(4, 0),
         memorySize:    memoryLog,
         valid:         true,
         writes:        true,
      },
      LOG3: {
         execute:       makeLog(3),
         gasCost:       makeGasLog(3),
         validateStack: makeStackFunc(5, 0),
         memorySize:    memoryLog,
         valid:         true,
         writes:        true,
      },
      LOG4: {
         execute:       makeLog(4),
         gasCost:       makeGasLog(4),
         validateStack: makeStackFunc(6, 0),
         memorySize:    memoryLog,
         valid:         true,
         writes:        true,
      },
      CREATE: {
         execute:       opCreate,
         gasCost:       gasCreate,
         validateStack: makeStackFunc(3, 1),
         memorySize:    memoryCreate,
         valid:         true,
         writes:        true,
         returns:       true,
      },
      CALL: {
         execute:       opCall,
         gasCost:       gasCall,
         validateStack: makeStackFunc(7, 1),
         memorySize:    memoryCall,
         valid:         true,
         returns:       true,
      },
      CALLCODE: {
         execute:       opCallCode,
         gasCost:       gasCallCode,
         validateStack: makeStackFunc(7, 1),
         memorySize:    memoryCall,
         valid:         true,
         returns:       true,
      },
      RETURN: {
         execute:       opReturn,
         gasCost:       gasReturn,
         validateStack: makeStackFunc(2, 0),
         memorySize:    memoryReturn,
         halts:         true,
         valid:         true,
      },
      SELFDESTRUCT: {
         execute:       opSuicide,
         gasCost:       gasSuicide,
         validateStack: makeStackFunc(1, 0),
         halts:         true,
         valid:         true,
         writes:        true,
      },
   }
}

吐槽一句:知道负整数需要花费多少gasg码

ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

越大的负整数(-1大于-2)1越多,会花费比正整数多得多的gas。

这是部分gas规则:

  • 每笔交易需要支付 21000 gas
  • 每笔交易的0字节或代码需要支付 4 gas
  • 每笔交易的非0字节或代码需要支付 68 gas

所以当我们在abi中看到很多0的时候不要惊讶,因为0比其它的价格要低18倍

我们来来看看gas设置的源码:

func gasMStore8(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   var overflow bool
   gas, err := memoryGasCost(mem, memorySize)
   if err != nil {
      return 0, errGasUintOverflow
   }
   if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
      return 0, errGasUintOverflow
   }
   return gas, nil
}

func gasMStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   var overflow bool
   gas, err := memoryGasCost(mem, memorySize)
   if err != nil {
      return 0, errGasUintOverflow
   }
   if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
      return 0, errGasUintOverflow
   }
   return gas, nil
}

func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
   var overflow bool
   gas, err := memoryGasCost(mem, memorySize)
   if err != nil {
      return 0, err
   }
   if gas, overflow = math.SafeAdd(gas, params.CreateGas); overflow {
      return 0, errGasUintOverflow
   }
   return gas, nil
}

这是部分计算gas价格的源码:从上面可以看到价格的具体计算过程,错误好像大多数都是整数溢出的错误

我们最后在看一下解释器:解释器用于运行基于Ethereum的合同,并将使用通过环境查询外部源的状态信息。解释器将根据传递的字节码运行VM配置。

据结构
    
    // Config are the configuration options for the Interpreter
    type Config struct {
        // Debug enabled debugging Interpreter options
        Debug bool
        // EnableJit enabled the JIT VM
        EnableJit bool
        // ForceJit forces the JIT VM
        ForceJit bool
        // Tracer is the op code logger
        Tracer Tracer
        // NoRecursion disabled Interpreter call, callcode,
        // delegate call and create.
        NoRecursion bool
        // Disable gas metering
        DisableGasMetering bool
        // Enable recording of SHA3/keccak preimages
        EnablePreimageRecording bool
        // JumpTable contains the EVM instruction table. This
        // may be left uninitialised and will be set to the default
        // table.
        JumpTable [256]operation
    }
    
    // Interpreter is used to run Ethereum based contracts and will utilise the
    // passed evmironment to query external sources for state information.
    // The Interpreter will run the byte code VM or JIT VM based on the passed
    // configuration.
    type Interpreter struct {
        evm      *EVM
        cfg      Config
        gasTable params.GasTable   // 标识了很多操作的Gas价格
        intPool  *intPool
    
        readOnly   bool   // Whether to throw on stateful modifications
        returnData []byte // Last CALL's return data for subsequent reuse 最后一个函数的返回值
    }

构造函数
    
    // NewInterpreter returns a new instance of the Interpreter.
    func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
        // We use the STOP instruction whether to see
        // the jump table was initialised. If it was not
        // we'll set the default jump table.
        // 用一个STOP指令测试JumpTable是否已经被初始化了, 如果没有被初始化,那么设置为默认值
        if !cfg.JumpTable[STOP].valid { 
            switch {
            case evm.ChainConfig().IsByzantium(evm.BlockNumber):
                cfg.JumpTable = byzantiumInstructionSet
            case evm.ChainConfig().IsHomestead(evm.BlockNumber):
                cfg.JumpTable = homesteadInstructionSet
            default:
                cfg.JumpTable = frontierInstructionSet
            }
        }
    
        return &Interpreter{
            evm:      evm,
            cfg:      cfg,
            gasTable: evm.ChainConfig().GasTable(evm.BlockNumber),
            intPool:  newIntPool(),
        }
    }


解释器一共就两个方法enforceRestrictions方法和Run方法.


    
    func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
        if in.evm.chainRules.IsByzantium {
            if in.readOnly {
                // If the interpreter is operating in readonly mode, make sure no
                // state-modifying operation is performed. The 3rd stack item
                // for a call operation is the value. Transferring value from one
                // account to the others means the state is modified and should also
                // return with an error.
                if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) {
                    return errWriteProtection
                }
            }
        }
        return nil
    }
    
    // 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.
    // 用给定的入参循环执行合约的代码,并返回返回的字节片段,如果发生错误则返回错误。
    // It's important to note that any errors returned by the interpreter should be
    // considered a revert-and-consume-all-gas operation. No error specific checks
    // should be handled to reduce complexity and errors further down the in.
    // 重要的是要注意,解释器返回的任何错误都会消耗全部gas。 为了减少复杂性,没有特别的错误处理流程。
    func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) {
        // Increment the call depth which is restricted to 1024
        in.evm.depth++
        defer func() { in.evm.depth-- }()
    
        // Reset the previous call's return data. It's unimportant to preserve the old buffer
        // as every returning call will return new data anyway.
        in.returnData = nil
    
        // Don't bother with the execution if there's no code.
        if len(contract.Code) == 0 {
            return nil, nil
        }
    
        codehash := contract.CodeHash // codehash is used when doing jump dest caching
        if codehash == (common.Hash{}) {
            codehash = crypto.Keccak256Hash(contract.Code)
        }
    
        var (
            op    OpCode        // current opcode
            mem   = NewMemory() // bound memory
            stack = newstack()  // local stack
            // For optimisation reason we're using uint64 as the program counter.
            // It's theoretically possible to go above 2^64. The YP defines the PC
            // to be uint256. Practically much less so feasible.
            pc   = uint64(0) // program counter
            cost uint64
            // copies used by tracer
            stackCopy = newstack() // stackCopy needed for Tracer since stack is mutated by 63/64 gas rule 
            pcCopy uint64 // needed for the deferred Tracer
            gasCopy uint64 // for Tracer to log gas remaining before execution
            logged bool // deferred Tracer should ignore already logged steps
        )
        contract.Input = input
    
        defer func() {
            if err != nil && !logged && in.cfg.Debug {
                in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err)
            }
        }()
    
        // The Interpreter main run loop (contextual). This loop runs until either an
        // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
        // the execution of one of the operations or until the done flag is set by the
        // parent context.
        // 解释器的主要循环, 直到遇到STOP,RETURN,SELFDESTRUCT指令被执行,或者是遇到任意错误,或者说done 标志被父context设置。
        for atomic.LoadInt32(&in.evm.abort) == 0 {
            // Get the memory location of pc
            // 难道下一个需要执行的指令
            op = contract.GetOp(pc)
    
            if in.cfg.Debug {
                logged = false
                pcCopy = uint64(pc)
                gasCopy = uint64(contract.Gas)
                stackCopy = newstack()
                for _, val := range stack.data {
                    stackCopy.push(val)
                }
            }
    
            // get the operation from the jump table matching the opcode
            // 通过JumpTable拿到对应的operation
            operation := in.cfg.JumpTable[op]
            // 这里检查了只读模式下面不能执行writes指令
            // staticCall的情况下会设置为readonly模式
            if err := in.enforceRestrictions(op, operation, stack); err != nil {
                return nil, err
            }
    
            // if the op is invalid abort the process and return an error
            if !operation.valid { //检查指令是否非法
                return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
            }
    
            // validate the stack and make sure there enough stack items available
            // to perform the operation
            // 检查是否有足够的堆栈空间。 包括入栈和出栈
            if err := operation.validateStack(stack); err != nil {
                return nil, err
            }
    
            var memorySize uint64
            // calculate the new memory size and expand the memory to fit
            // the operation
            if operation.memorySize != nil { // 计算内存使用量,需要收费
                memSize, overflow := bigUint64(operation.memorySize(stack))
                if overflow {
                    return nil, errGasUintOverflow
                }
                // memory is expanded in words of 32 bytes. Gas
                // is also calculated in words.
                if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
                    return nil, errGasUintOverflow
                }
            }
    
            if !in.cfg.DisableGasMetering { //这个参数在本地模拟执行的时候比较有用,可以不消耗或者检查GAS执行交易并得到返回结果
                // consume the gas and return an error if not enough gas is available.
                // cost is explicitly set so that the capture state defer method cas get the proper cost
                // 计算gas的Cost 并使用,如果不够,就返回OutOfGas错误。
                cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)
                if err != nil || !contract.UseGas(cost) {
                    return nil, ErrOutOfGas
                }
            }
            if memorySize > 0 { //扩大内存范围
                mem.Resize(memorySize)
            }
    
            if in.cfg.Debug {
                in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err)
                logged = true
            }
    
            // execute the operation
            // 执行命令
            res, err := operation.execute(&pc, in.evm, contract, mem, stack)
            // verifyPool is a build flag. Pool verification makes sure the integrity
            // of the integer pool by comparing values to a default value.
            if verifyPool {
                verifyIntegerPool(in.intPool)
            }
            // 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++
            }
        }
        return nil, nil
    }

总结:与智能合约交互,你需要发送原始字节。他会进行一些映射,然后进行运算。方法调用实际上不存在,这是ABI创造的一个很有意思假象,然我们以为自己在调用方法,实际上只是执行了一段指令。

你可能感兴趣的:(ETH)