github.com/klaytn/klaytn@v1.10.2/blockchain/vm/interpreter.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from core/vm/interpreter.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package vm 22 23 import ( 24 "fmt" 25 "hash" 26 "sync/atomic" 27 28 "github.com/klaytn/klaytn/common" 29 "github.com/klaytn/klaytn/common/math" 30 "github.com/klaytn/klaytn/kerrors" 31 "github.com/klaytn/klaytn/params" 32 ) 33 34 // Config are the configuration options for the Interpreter 35 type Config struct { 36 Debug bool // Enables debugging 37 Tracer Tracer // Opcode logger 38 NoRecursion bool // Disables call, callcode, delegate call and create 39 EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages 40 41 JumpTable [256]*operation // EVM instruction table, automatically populated if unset 42 43 // RunningEVM is to indicate the running EVM and used to stop the EVM. 44 RunningEVM chan *EVM 45 46 // UseOpcodeComputationCost is to enable applying the opcode computation cost limit. 47 UseOpcodeComputationCost bool 48 49 // Enables collecting internal transaction data during processing a block 50 EnableInternalTxTracing bool 51 52 // Prefetching is true if the EVM is used for prefetching. 53 Prefetching bool 54 55 // Additional EIPs that are to be enabled 56 ExtraEips []int 57 } 58 59 // keccakState wraps sha3.state. In addition to the usual hash methods, it also supports 60 // Read to get a variable amount of data from the hash state. Read is faster than Sum 61 // because it doesn't copy the internal state, but also modifies the internal state. 62 type keccakState interface { 63 hash.Hash 64 Read([]byte) (int, error) 65 } 66 67 // Interpreter is used to run Klaytn based contracts and will utilise the 68 // passed environment to query external sources for state information. 69 // The Interpreter will run the byte code VM based on the passed 70 // configuration. 71 type Interpreter struct { 72 evm *EVM 73 cfg *Config 74 75 intPool *intPool 76 77 hasher keccakState // Keccak256 hasher instance shared across opcodes 78 hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes 79 80 readOnly bool // Whether to throw on stateful modifications 81 returnData []byte // Last CALL's return data for subsequent reuse 82 } 83 84 // NewEVMInterpreter returns a new instance of the Interpreter. 85 func NewEVMInterpreter(evm *EVM, cfg *Config) *Interpreter { 86 // We use the STOP instruction whether to see 87 // the jump table was initialised. If it was not 88 // we'll set the default jump table. 89 if cfg.JumpTable[STOP] == nil { 90 var jt JumpTable 91 switch { 92 case evm.chainRules.IsKore: 93 jt = KoreInstructionSet 94 case evm.chainRules.IsLondon: 95 jt = LondonInstructionSet 96 case evm.chainRules.IsIstanbul: 97 jt = IstanbulInstructionSet 98 default: 99 jt = ConstantinopleInstructionSet 100 } 101 for i, eip := range cfg.ExtraEips { 102 if err := EnableEIP(eip, &jt); err != nil { 103 // Disable it, so caller can check if it's activated or not 104 cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...) 105 logger.Error("EIP activation failed", "eip", eip, "error", err) 106 } 107 } 108 cfg.JumpTable = jt 109 } 110 111 return &Interpreter{ 112 evm: evm, 113 cfg: cfg, 114 } 115 } 116 117 /////////////////////////////////////////////////////// 118 // OpcodeComputationCostLimit: The below code is commented and will be usd for debugging purposes. 119 //var ( 120 // prevOp OpCode 121 // globalTimer = time.Now() 122 // opCnt = make([]uint64, 256) 123 // opTime = make([]uint64, 256) 124 // precompiledCnt = make([]uint64, 16) 125 // precompiledTime = make([]uint64, 16) 126 // opDebug = true 127 //) 128 /////////////////////////////////////////////////////// 129 130 // Run loops and evaluates the contract's code with the given input data and returns 131 // the return byte-slice and an error if one occurred. 132 // 133 // It's important to note that any errors returned by the interpreter should be 134 // considered a revert-and-consume-all-gas operation except for 135 // ErrExecutionReverted which means revert-and-keep-gas-left. 136 func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { 137 if in.intPool == nil { 138 in.intPool = poolOfIntPools.get() 139 defer func() { 140 poolOfIntPools.put(in.intPool) 141 in.intPool = nil 142 }() 143 } 144 145 /////////////////////////////////////////////////////// 146 // OpcodeComputationCostLimit: The below code is commented and will be usd for debugging purposes. 147 //if opDebug { 148 // if in.evm.depth == 0 { 149 // for i := 0; i< 256; i++ { 150 // opCnt[i] = 0 151 // opTime[i] = 0 152 // } 153 // prevOp = 0 154 // defer func() { 155 // for i := 0; i < 256; i++ { 156 // if opCnt[i] > 0 { 157 // fmt.Println("op", OpCode(i).String(), "computationCost", in.cfg.JumpTable[i].computationCost, "cnt", opCnt[i], "avg", opTime[i]/opCnt[i]) 158 // } 159 // } 160 // for i := 0; i < 16; i++ { 161 // if precompiledCnt[i] > 0 { 162 // fmt.Println("precompiled contract addr", i, "cnt", precompiledCnt[i], "avg", precompiledTime[i]/precompiledCnt[i]) 163 // } 164 // } 165 // }() 166 // } 167 //} 168 /////////////////////////////////////////////////////// 169 170 // Increment the call depth which is restricted to 1024 171 in.evm.depth++ 172 defer func() { in.evm.depth-- }() 173 174 // Reset the previous call's return data. It's unimportant to preserve the old buffer 175 // as every returning call will return new data anyway. 176 in.returnData = nil 177 178 // Don't bother with the execution if there's no code. 179 if len(contract.Code) == 0 { 180 return nil, nil 181 } 182 183 var ( 184 op OpCode // current opcode 185 mem = NewMemory() // bound memory 186 stack = newstack() // local stack 187 // For optimisation reason we're using uint64 as the program counter. 188 // It's theoretically possible to go above 2^64. The YP defines the PC 189 // to be uint256. Practically much less so feasible. 190 pc = uint64(0) // program counter 191 cost uint64 192 // copies used by tracer 193 pcCopy uint64 // needed for the deferred Tracer 194 gasCopy uint64 // for Tracer to log gas remaining before execution 195 logged bool // deferred Tracer should ignore already logged steps 196 res []byte // result of the opcode execution function 197 allocatedMemorySize = uint64(mem.Len()) // Currently allocated memory size 198 ) 199 contract.Input = input 200 201 // Reclaim the stack as an int pool when the execution stops 202 defer func() { in.intPool.put(stack.data...) }() 203 204 if in.cfg.Debug { 205 defer func() { 206 if err != nil { 207 if !logged { 208 in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 209 } else { 210 in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 211 } 212 } 213 }() 214 } 215 216 // The Interpreter main run loop (contextual). This loop runs until either an 217 // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during 218 // the execution of one of the operations or until the done flag is set by the 219 // parent context. 220 for atomic.LoadInt32(&in.evm.abort) == 0 { 221 if in.cfg.Debug { 222 // Capture pre-execution values for tracing. 223 logged, pcCopy, gasCopy = false, pc, contract.Gas 224 } 225 226 /////////////////////////////////////////////////////// 227 // OpcodeComputationCostLimit: The below code is commented and will be usd for debugging purposes. 228 //if opDebug { 229 // prevOp = op 230 //} 231 /////////////////////////////////////////////////////// 232 // Get the operation from the jump table and validate the stack to ensure there are 233 // enough stack items available to perform the operation. 234 op = contract.GetOp(pc) 235 operation := in.cfg.JumpTable[op] 236 if operation == nil { 237 return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) // TODO-Klaytn-Issue615 238 } 239 // Validate stack 240 if sLen := stack.len(); sLen < operation.minStack { 241 return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack) 242 } else if sLen > operation.maxStack { 243 return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack) 244 } 245 // If the operation is valid, enforce and write restrictions 246 if in.readOnly { 247 // If the interpreter is operating in readonly mode, make sure no 248 // state-modifying operation is performed. The 3rd stack item 249 // for a call operation is the value. Transferring value from one 250 // account to the others means the state is modified and should also 251 // return with an error. 252 if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) { 253 return nil, ErrWriteProtection 254 } 255 } 256 257 // Static portion of gas 258 cost = operation.constantGas // For tracing 259 if !contract.UseGas(operation.constantGas) { 260 return nil, kerrors.ErrOutOfGas 261 } 262 263 // We limit tx's execution time using the sum of computation cost of opcodes. 264 if in.evm.vmConfig.UseOpcodeComputationCost { 265 /////////////////////////////////////////////////////// 266 // OpcodeComputationCostLimit: The below code is commented and will be usd for debugging purposes. 267 //if opDebug && prevOp > 0 { 268 // elapsed := uint64(time.Since(globalTimer).Nanoseconds()) 269 // fmt.Println("[", in.evm.depth, "]", "prevop", prevOp.String(), "-", op.String(), "computationCost", in.cfg.JumpTable[prevOp].computationCost, "total", in.evm.opcodeComputationCostSum, "elapsed", elapsed) 270 // opTime[prevOp] += elapsed 271 // opCnt[prevOp] += 1 272 //} 273 //globalTimer = time.Now() 274 /////////////////////////////////////////////////////// 275 in.evm.opcodeComputationCostSum += operation.computationCost 276 if in.evm.opcodeComputationCostSum > params.OpcodeComputationCostLimit { 277 return nil, ErrOpcodeComputationCostLimitReached 278 } 279 } 280 var memorySize uint64 281 var extraSize uint64 282 // calculate the new memory size and expand the memory to fit 283 // the operation 284 // Memory check needs to be done prior to evaluating the dynamic gas portion, 285 // to detect calculation overflows 286 if operation.memorySize != nil { 287 memSize, overflow := operation.memorySize(stack) 288 if overflow { 289 return nil, errGasUintOverflow // TODO-Klaytn-Issue615 290 } 291 // memory is expanded in words of 32 bytes. Gas 292 // is also calculated in words. 293 if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { 294 return nil, errGasUintOverflow // TODO-Klaytn-Issue615 295 } 296 if allocatedMemorySize < memorySize { 297 extraSize = memorySize - allocatedMemorySize 298 } 299 } 300 // Dynamic portion of gas 301 // consume the gas and return an error if not enough gas is available. 302 // cost is explicitly set so that the capture state defer method can get the proper cost 303 if operation.dynamicGas != nil { 304 var dynamicCost uint64 305 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) 306 cost += dynamicCost // total cost, for debug tracing 307 if err != nil || !contract.UseGas(dynamicCost) { 308 return nil, kerrors.ErrOutOfGas // TODO-Klaytn-Issue615 309 } 310 } 311 if extraSize > 0 { 312 mem.Increase(extraSize) 313 allocatedMemorySize = uint64(mem.Len()) 314 } 315 316 if in.cfg.Debug { 317 in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) 318 logged = true 319 } 320 321 // execute the operation 322 res, err = operation.execute(&pc, in.evm, contract, mem, stack) 323 // verifyPool is a build flag. Pool verification makes sure the integrity 324 // of the integer pool by comparing values to a default value. 325 if verifyPool { 326 verifyIntegerPool(in.intPool) 327 } 328 // if the operation clears the return data (e.g. it has returning data) 329 // set the last return to the result of the operation. 330 if operation.returns { 331 in.returnData = res 332 } 333 334 switch { 335 case err != nil: 336 return nil, err // TODO-Klaytn-Issue615 337 case operation.reverts: 338 return res, ErrExecutionReverted // TODO-Klaytn-Issue615 339 case operation.halts: 340 return res, nil 341 case !operation.jumps: 342 pc++ 343 } 344 } 345 346 abort := atomic.LoadInt32(&in.evm.abort) 347 if (abort & CancelByTotalTimeLimit) != 0 { 348 return nil, ErrTotalTimeLimitReached // TODO-Klaytn-Issue615 349 } 350 return nil, nil 351 }