github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/core/vm/interpreter.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package vm 13 14 import ( 15 "fmt" 16 "sync/atomic" 17 18 "github.com/Sberex/go-sberex/common/math" 19 "github.com/Sberex/go-sberex/params" 20 ) 21 22 // Config are the configuration options for the Interpreter 23 type Config struct { 24 // Debug enabled debugging Interpreter options 25 Debug bool 26 // EnableJit enabled the JIT VM 27 EnableJit bool 28 // ForceJit forces the JIT VM 29 ForceJit bool 30 // Tracer is the op code logger 31 Tracer Tracer 32 // NoRecursion disabled Interpreter call, callcode, 33 // delegate call and create. 34 NoRecursion bool 35 // Disable gas metering 36 DisableGasMetering bool 37 // Enable recording of SHA3/keccak preimages 38 EnablePreimageRecording bool 39 // JumpTable contains the EVM instruction table. This 40 // may be left uninitialised and will be set to the default 41 // table. 42 JumpTable [256]operation 43 } 44 45 // Interpreter is used to run Sberex based contracts and will utilise the 46 // passed evmironment to query external sources for state information. 47 // The Interpreter will run the byte code VM or JIT VM based on the passed 48 // configuration. 49 type Interpreter struct { 50 evm *EVM 51 cfg Config 52 gasTable params.GasTable 53 intPool *intPool 54 55 readOnly bool // Whether to throw on stateful modifications 56 returnData []byte // Last CALL's return data for subsequent reuse 57 } 58 59 // NewInterpreter returns a new instance of the Interpreter. 60 func NewInterpreter(evm *EVM, cfg Config) *Interpreter { 61 // We use the STOP instruction whether to see 62 // the jump table was initialised. If it was not 63 // we'll set the default jump table. 64 if !cfg.JumpTable[STOP].valid { 65 switch { 66 case evm.ChainConfig().IsConstantinople(evm.BlockNumber): 67 cfg.JumpTable = constantinopleInstructionSet 68 case evm.ChainConfig().IsByzantium(evm.BlockNumber): 69 cfg.JumpTable = byzantiumInstructionSet 70 case evm.ChainConfig().IsHomestead(evm.BlockNumber): 71 cfg.JumpTable = homesteadInstructionSet 72 default: 73 cfg.JumpTable = frontierInstructionSet 74 } 75 } 76 77 return &Interpreter{ 78 evm: evm, 79 cfg: cfg, 80 gasTable: evm.ChainConfig().GasTable(evm.BlockNumber), 81 intPool: newIntPool(), 82 } 83 } 84 85 func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error { 86 if in.evm.chainRules.IsByzantium { 87 if in.readOnly { 88 // If the interpreter is operating in readonly mode, make sure no 89 // state-modifying operation is performed. The 3rd stack item 90 // for a call operation is the value. Transferring value from one 91 // account to the others means the state is modified and should also 92 // return with an error. 93 if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) { 94 return errWriteProtection 95 } 96 } 97 } 98 return nil 99 } 100 101 // Run loops and evaluates the contract's code with the given input data and returns 102 // the return byte-slice and an error if one occurred. 103 // 104 // It's important to note that any errors returned by the interpreter should be 105 // considered a revert-and-consume-all-gas operation except for 106 // errExecutionReverted which means revert-and-keep-gas-left. 107 func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { 108 // Increment the call depth which is restricted to 1024 109 in.evm.depth++ 110 defer func() { in.evm.depth-- }() 111 112 // Reset the previous call's return data. It's unimportant to preserve the old buffer 113 // as every returning call will return new data anyway. 114 in.returnData = nil 115 116 // Don't bother with the execution if there's no code. 117 if len(contract.Code) == 0 { 118 return nil, nil 119 } 120 121 var ( 122 op OpCode // current opcode 123 mem = NewMemory() // bound memory 124 stack = newstack() // local stack 125 // For optimisation reason we're using uint64 as the program counter. 126 // It's theoretically possible to go above 2^64. The YP defines the PC 127 // to be uint256. Practically much less so feasible. 128 pc = uint64(0) // program counter 129 cost uint64 130 // copies used by tracer 131 pcCopy uint64 // needed for the deferred Tracer 132 gasCopy uint64 // for Tracer to log gas remaining before execution 133 logged bool // deferred Tracer should ignore already logged steps 134 ) 135 contract.Input = input 136 137 if in.cfg.Debug { 138 defer func() { 139 if err != nil { 140 if !logged { 141 in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 142 } else { 143 in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 144 } 145 } 146 }() 147 } 148 // The Interpreter main run loop (contextual). This loop runs until either an 149 // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during 150 // the execution of one of the operations or until the done flag is set by the 151 // parent context. 152 for atomic.LoadInt32(&in.evm.abort) == 0 { 153 if in.cfg.Debug { 154 // Capture pre-execution values for tracing. 155 logged, pcCopy, gasCopy = false, pc, contract.Gas 156 } 157 158 // Get the operation from the jump table and validate the stack to ensure there are 159 // enough stack items available to perform the operation. 160 op = contract.GetOp(pc) 161 operation := in.cfg.JumpTable[op] 162 if !operation.valid { 163 return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) 164 } 165 if err := operation.validateStack(stack); err != nil { 166 return nil, err 167 } 168 // If the operation is valid, enforce and write restrictions 169 if err := in.enforceRestrictions(op, operation, stack); err != nil { 170 return nil, err 171 } 172 173 var memorySize uint64 174 // calculate the new memory size and expand the memory to fit 175 // the operation 176 if operation.memorySize != nil { 177 memSize, overflow := bigUint64(operation.memorySize(stack)) 178 if overflow { 179 return nil, errGasUintOverflow 180 } 181 // memory is expanded in words of 32 bytes. Gas 182 // is also calculated in words. 183 if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { 184 return nil, errGasUintOverflow 185 } 186 } 187 188 if !in.cfg.DisableGasMetering { 189 // consume the gas and return an error if not enough gas is available. 190 // cost is explicitly set so that the capture state defer method cas get the proper cost 191 cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize) 192 if err != nil || !contract.UseGas(cost) { 193 return nil, ErrOutOfGas 194 } 195 } 196 if memorySize > 0 { 197 mem.Resize(memorySize) 198 } 199 200 if in.cfg.Debug { 201 in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 202 logged = true 203 } 204 205 // execute the operation 206 res, err := operation.execute(&pc, in.evm, contract, mem, stack) 207 // verifyPool is a build flag. Pool verification makes sure the integrity 208 // of the integer pool by comparing values to a default value. 209 if verifyPool { 210 verifyIntegerPool(in.intPool) 211 } 212 // if the operation clears the return data (e.g. it has returning data) 213 // set the last return to the result of the operation. 214 if operation.returns { 215 in.returnData = res 216 } 217 218 switch { 219 case err != nil: 220 return nil, err 221 case operation.reverts: 222 return res, errExecutionReverted 223 case operation.halts: 224 return res, nil 225 case !operation.jumps: 226 pc++ 227 } 228 } 229 return nil, nil 230 }