github.com/klaytn/klaytn@v1.12.1/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 "time" 28 29 "github.com/klaytn/klaytn/common" 30 "github.com/klaytn/klaytn/common/math" 31 "github.com/klaytn/klaytn/kerrors" 32 "github.com/klaytn/klaytn/params" 33 ) 34 35 // Config are the configuration options for the Interpreter 36 type Config struct { 37 Debug bool // Enables debugging 38 Tracer Tracer // Opcode logger 39 NoRecursion bool // Disables call, callcode, delegate call and create 40 EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages 41 42 JumpTable JumpTable // EVM instruction table, automatically populated if unset 43 44 // RunningEVM is to indicate the running EVM and used to stop the EVM. 45 RunningEVM chan *EVM 46 47 // ComputationCostLimit is the limit of the total computation cost of a transaction. Set infinite to disable the computation cost limit. 48 ComputationCostLimit uint64 49 50 // Enables collecting internal transaction data during processing a block 51 EnableInternalTxTracing bool 52 53 // Enables collecting and printing opcode execution time 54 EnableOpDebug bool 55 56 // Prefetching is true if the EVM is used for prefetching. 57 Prefetching bool 58 59 // Additional EIPs that are to be enabled 60 ExtraEips []int 61 } 62 63 // ScopeContext contains the things that are per-call, such as stack and memory, 64 // but not transients like pc and gas 65 type ScopeContext struct { 66 Memory *Memory 67 Stack *Stack 68 Contract *Contract 69 } 70 71 // keccakState wraps sha3.state. In addition to the usual hash methods, it also supports 72 // Read to get a variable amount of data from the hash state. Read is faster than Sum 73 // because it doesn't copy the internal state, but also modifies the internal state. 74 type keccakState interface { 75 hash.Hash 76 Read([]byte) (int, error) 77 } 78 79 // EVMInterpreter is used to run Klaytn based contracts and will utilise the 80 // passed environment to query external sources for state information. 81 // The EVMInterpreter will run the byte code VM based on the passed 82 // configuration. 83 type EVMInterpreter struct { 84 evm *EVM 85 cfg *Config 86 87 hasher keccakState // Keccak256 hasher instance shared across opcodes 88 hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes 89 90 readOnly bool // Whether to throw on stateful modifications 91 returnData []byte // Last CALL's return data for subsequent reuse 92 } 93 94 // NewEVMInterpreter returns a new instance of the Interpreter. 95 func NewEVMInterpreter(evm *EVM) *EVMInterpreter { 96 // We use the STOP instruction whether to see 97 // the jump table was initialised. If it was not 98 // we'll set the default jump table. 99 cfg := evm.Config 100 if cfg.JumpTable[STOP] == nil { 101 var jt JumpTable 102 switch { 103 case evm.chainRules.IsCancun: 104 jt = CancunInstructionSet 105 case evm.chainRules.IsShanghai: 106 jt = ShanghaiInstructionSet 107 case evm.chainRules.IsKore: 108 jt = KoreInstructionSet 109 case evm.chainRules.IsLondon: 110 jt = LondonInstructionSet 111 case evm.chainRules.IsIstanbul: 112 jt = IstanbulInstructionSet 113 default: 114 jt = ConstantinopleInstructionSet 115 } 116 for i, eip := range cfg.ExtraEips { 117 if err := EnableEIP(eip, &jt); err != nil { 118 // Disable it, so caller can check if it's activated or not 119 cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...) 120 logger.Error("EIP activation failed", "eip", eip, "error", err) 121 } 122 } 123 cfg.JumpTable = jt 124 } 125 126 // When setting computation cost limit value, priority is given to the original value, override experimental value, 127 // and then the limit value specified for each hard fork. If the original value is not infinite or 128 // there is no override value, the next priority value is used. 129 // Cautious, the infinite value is only applicable for specific API calls. (e.g. call/estimateGas/estimateComputationGas) 130 if cfg.ComputationCostLimit == params.OpcodeComputationCostLimitInfinite { 131 return &EVMInterpreter{evm: evm, cfg: cfg} 132 } 133 // Override the computation cost with an experiment value 134 if params.OpcodeComputationCostLimitOverride != 0 { 135 cfg.ComputationCostLimit = params.OpcodeComputationCostLimitOverride 136 return &EVMInterpreter{evm: evm, cfg: cfg} 137 } 138 // Set the opcode computation cost limit by the default value 139 switch { 140 case evm.chainRules.IsCancun: 141 cfg.ComputationCostLimit = uint64(params.OpcodeComputationCostLimitCancun) 142 default: 143 cfg.ComputationCostLimit = uint64(params.OpcodeComputationCostLimit) 144 } 145 return &EVMInterpreter{evm: evm, cfg: cfg} 146 } 147 148 // count values and execution time of the opcodes are collected until the node is turned off. 149 var ( 150 opCnt = make([]uint64, 256) 151 opTime = make([]uint64, 256) 152 ) 153 154 // Run loops and evaluates the contract's code with the given input data and returns 155 // the return byte-slice and an error if one occurred. 156 // 157 // It's important to note that any errors returned by the interpreter should be 158 // considered a revert-and-consume-all-gas operation except for 159 // ErrExecutionReverted which means revert-and-keep-gas-left. 160 func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { 161 // Increment the call depth which is restricted to 1024 162 in.evm.depth++ 163 defer func() { in.evm.depth-- }() 164 165 // Reset the previous call's return data. It's unimportant to preserve the old buffer 166 // as every returning call will return new data anyway. 167 in.returnData = nil 168 169 // Don't bother with the execution if there's no code. 170 if len(contract.Code) == 0 { 171 return nil, nil 172 } 173 174 var ( 175 op OpCode // current opcode 176 mem = NewMemory() // bound memory 177 stack = newstack() // local stack 178 callContext = &ScopeContext{ 179 Memory: mem, 180 Stack: stack, 181 Contract: contract, 182 } 183 // For optimisation reason we're using uint64 as the program counter. 184 // It's theoretically possible to go above 2^64. The YP defines the PC 185 // to be uint256. Practically much less so feasible. 186 pc = uint64(0) // program counter 187 cost uint64 188 // copies used by tracer 189 pcCopy uint64 // needed for the deferred Tracer 190 gasCopy uint64 // for Tracer to log gas remaining before execution 191 logged bool // deferred Tracer should ignore already logged steps 192 res []byte // result of the opcode execution function 193 allocatedMemorySize = uint64(mem.Len()) // Currently allocated memory size 194 195 // used for collecting opcode execution time 196 opExecStart time.Time 197 ) 198 contract.Input = input 199 200 if in.cfg.Debug { 201 defer func() { 202 if err != nil { 203 if !logged { 204 in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) 205 } else { 206 in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) 207 } 208 } 209 }() 210 } 211 212 // The Interpreter main run loop (contextual). This loop runs until either an 213 // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during 214 // the execution of one of the operations or until the done flag is set by the 215 // parent context. 216 for atomic.LoadInt32(&in.evm.abort) == 0 { 217 if in.evm.Config.EnableOpDebug { 218 opExecStart = time.Now() 219 } 220 if in.cfg.Debug { 221 // Capture pre-execution values for tracing. 222 logged, pcCopy, gasCopy = false, pc, contract.Gas 223 } 224 225 // Get the operation from the jump table and validate the stack to ensure there are 226 // enough stack items available to perform the operation. 227 op = contract.GetOp(pc) 228 operation := in.cfg.JumpTable[op] 229 if operation == nil { 230 return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) // TODO-Klaytn-Issue615 231 } 232 // Validate stack 233 if sLen := stack.len(); sLen < operation.minStack { 234 return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack) 235 } else if sLen > operation.maxStack { 236 return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack) 237 } 238 239 // Static portion of gas 240 cost = operation.constantGas // For tracing 241 if !contract.UseGas(operation.constantGas) { 242 return nil, kerrors.ErrOutOfGas 243 } 244 245 // We limit tx's execution time using the sum of computation cost of opcodes. 246 in.evm.opcodeComputationCostSum += operation.computationCost 247 if in.evm.opcodeComputationCostSum > in.evm.Config.ComputationCostLimit { 248 return nil, ErrOpcodeComputationCostLimitReached 249 } 250 var memorySize uint64 251 var extraSize uint64 252 // calculate the new memory size and expand the memory to fit 253 // the operation 254 // Memory check needs to be done prior to evaluating the dynamic gas portion, 255 // to detect calculation overflows 256 if operation.memorySize != nil { 257 memSize, overflow := operation.memorySize(stack) 258 if overflow { 259 return nil, errGasUintOverflow // TODO-Klaytn-Issue615 260 } 261 // memory is expanded in words of 32 bytes. Gas 262 // is also calculated in words. 263 if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { 264 return nil, errGasUintOverflow // TODO-Klaytn-Issue615 265 } 266 if allocatedMemorySize < memorySize { 267 extraSize = memorySize - allocatedMemorySize 268 } 269 } 270 // Dynamic portion of gas 271 // consume the gas and return an error if not enough gas is available. 272 // cost is explicitly set so that the capture state defer method can get the proper cost 273 if operation.dynamicGas != nil { 274 var dynamicCost uint64 275 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) 276 cost += dynamicCost // total cost, for debug tracing 277 if err != nil || !contract.UseGas(dynamicCost) { 278 return nil, kerrors.ErrOutOfGas // TODO-Klaytn-Issue615 279 } 280 } 281 if extraSize > 0 { 282 mem.Increase(extraSize) 283 allocatedMemorySize = uint64(mem.Len()) 284 } 285 286 if in.cfg.Debug { 287 in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.evm.depth, err) 288 logged = true 289 } 290 291 // execute the operation 292 res, err = operation.execute(&pc, in, &ScopeContext{mem, stack, contract}) 293 if in.evm.Config.EnableOpDebug { 294 opTime[op] += uint64(time.Since(opExecStart).Nanoseconds()) 295 opCnt[op] += 1 296 } 297 if err != nil { 298 break 299 } 300 pc++ 301 } 302 303 abort := atomic.LoadInt32(&in.evm.abort) 304 if (abort & CancelByTotalTimeLimit) != 0 { 305 return nil, ErrTotalTimeLimitReached // TODO-Klaytn-Issue615 306 } 307 if err == errStopToken { 308 err = nil // clear stop token error 309 } 310 311 return res, err 312 } 313 314 func PrintOpCodeExecTime() { 315 logger.Info("Printing the execution time of the opcodes during this node operation") 316 for i := 0; i < 256; i++ { 317 if opCnt[i] > 0 { 318 logger.Info("op "+OpCode(i).String(), "cnt", opCnt[i], "avg", opTime[i]/opCnt[i]) 319 } 320 } 321 }