github.com/amazechain/amc@v0.1.3/internal/vm/interpreter.go (about) 1 // Copyright 2023 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The AmazeChain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 package vm 17 18 import ( 19 "github.com/amazechain/amc/common/math" 20 "github.com/amazechain/amc/common/types" 21 "github.com/amazechain/amc/internal/vm/stack" 22 "github.com/amazechain/amc/log" 23 "github.com/amazechain/amc/params" 24 "hash" 25 "sync" 26 ) 27 28 // Config are the configuration options for the Interpreter 29 type Config struct { 30 Debug bool // Enables debugging 31 Tracer EVMLogger // Opcode logger 32 NoRecursion bool // Disables call, callcode, delegate call and create 33 NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) 34 SkipAnalysis bool // Whether we can skip jumpdest analysis based on the checked history 35 TraceJumpDest bool // Print transaction hashes where jumpdest analysis was useful 36 NoReceipts bool // Do not calculate receipts 37 ReadOnly bool // Do no perform any block finalisation 38 StatelessExec bool // true is certain conditions (like state trie root hash matching) need to be relaxed for stateless EVM execution 39 RestoreState bool // Revert all changes made to the state (useful for constant system calls) 40 41 ExtraEips []int // Additional EIPS that are to be enabled 42 } 43 44 var pool = sync.Pool{ 45 New: func() any { 46 return NewMemory() 47 }, 48 } 49 50 func (vmConfig *Config) HasEip3860(rules *params.Rules) bool { 51 for _, eip := range vmConfig.ExtraEips { 52 if eip == 3860 { 53 return true 54 } 55 } 56 return rules.IsShanghai 57 } 58 59 // Interpreter is used to run Ethereum based contracts and will utilise the 60 // passed environment to query external sources for state information. 61 // The Interpreter will run the byte code VM based on the passed 62 // configuration. 63 type Interpreter interface { 64 // Run loops and evaluates the contract's code with the given input data and returns 65 // the return byte-slice and an error if one occurred. 66 Run(contract *Contract, input []byte, static bool) ([]byte, error) 67 68 // `Depth` returns the current call stack's depth. 69 Depth() int 70 } 71 72 // ScopeContext contains the things that are per-call, such as stack and memory, 73 // but not transients like pc and gas 74 type ScopeContext struct { 75 Memory *Memory 76 Stack *stack.Stack 77 Contract *Contract 78 } 79 80 // keccakState wraps sha3.state. In addition to the usual hash methods, it also supports 81 // Read to get a variable amount of data from the hash state. Read is faster than Sum 82 // because it doesn't copy the internal state, but also modifies the internal state. 83 type keccakState interface { 84 hash.Hash 85 Read([]byte) (int, error) 86 } 87 88 // EVMInterpreter represents an EVM interpreter 89 type EVMInterpreter struct { 90 *VM 91 jt *JumpTable // EVM instruction table 92 depth int 93 } 94 95 // structcheck doesn't see embedding 96 // 97 //nolint:structcheck 98 type VM struct { 99 evm VMInterpreter 100 cfg Config 101 102 hasher keccakState // Keccak256 hasher instance shared across opcodes 103 hasherBuf types.Hash // Keccak256 hasher result array shared across opcodes 104 105 readOnly bool // Whether to throw on stateful modifications 106 returnData []byte // Last CALL's return data for subsequent reuse 107 } 108 109 func copyJumpTable(jt *JumpTable) *JumpTable { 110 var copy JumpTable 111 for i, op := range jt { 112 if op != nil { 113 opCopy := *op 114 copy[i] = &opCopy 115 } 116 } 117 return © 118 } 119 120 // NewEVMInterpreter returns a new instance of the Interpreter. 121 func NewEVMInterpreter(evm VMInterpreter, cfg Config) *EVMInterpreter { 122 var jt *JumpTable 123 switch { 124 case evm.ChainRules().IsPrague: 125 jt = &pragueInstructionSet 126 case evm.ChainRules().IsCancun: 127 jt = &cancunInstructionSet 128 case evm.ChainRules().IsShanghai: 129 jt = &shanghaiInstructionSet 130 case evm.ChainRules().IsLondon: 131 jt = &londonInstructionSet 132 case evm.ChainRules().IsBerlin: 133 jt = &berlinInstructionSet 134 case evm.ChainRules().IsIstanbul: 135 jt = &istanbulInstructionSet 136 case evm.ChainRules().IsConstantinople: 137 jt = &constantinopleInstructionSet 138 case evm.ChainRules().IsByzantium: 139 jt = &byzantiumInstructionSet 140 case evm.ChainRules().IsSpuriousDragon: 141 jt = &spuriousDragonInstructionSet 142 case evm.ChainRules().IsTangerineWhistle: 143 jt = &tangerineWhistleInstructionSet 144 case evm.ChainRules().IsHomestead: 145 jt = &homesteadInstructionSet 146 default: 147 jt = &frontierInstructionSet 148 } 149 if len(cfg.ExtraEips) > 0 { 150 jt = copyJumpTable(jt) 151 for i, eip := range cfg.ExtraEips { 152 if err := EnableEIP(eip, jt); err != nil { 153 // Disable it, so caller can check if it's activated or not 154 cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...) 155 log.Error("EIP activation failed", "eip", eip, "err", err) 156 } 157 } 158 } 159 160 return &EVMInterpreter{ 161 VM: &VM{ 162 evm: evm, 163 cfg: cfg, 164 }, 165 jt: jt, 166 } 167 } 168 169 func (in *EVMInterpreter) decrementDepth() { in.depth-- } 170 171 // Run loops and evaluates the contract's code with the given input data and returns 172 // the return byte-slice and an error if one occurred. 173 // 174 // It's important to note that any errors returned by the interpreter should be 175 // considered a revert-and-consume-all-gas operation except for 176 // ErrExecutionReverted which means revert-and-keep-gas-left. 177 func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { 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 // Increment the call depth which is restricted to 1024 184 in.depth++ 185 defer in.decrementDepth() 186 187 // Make sure the readOnly is only set if we aren't in readOnly yet. 188 // This makes also sure that the readOnly flag isn't removed for child calls. 189 if readOnly && !in.readOnly { 190 in.readOnly = true 191 defer func() { in.readOnly = false }() 192 } 193 194 // Reset the previous call's return data. It's unimportant to preserve the old buffer 195 // as every returning call will return new data anyway. 196 in.returnData = nil 197 198 var ( 199 op OpCode // current opcode 200 mem = pool.Get().(*Memory) 201 locStack = stack.New() 202 callContext = &ScopeContext{ 203 Memory: mem, 204 Stack: locStack, 205 Contract: contract, 206 } 207 // For optimisation reason we're using uint64 as the program counter. 208 // It's theoretically possible to go above 2^64. The YP defines the PC 209 // to be uint256. Practically much less so feasible. 210 _pc = uint64(0) // program counter 211 pc = &_pc // program counter 212 cost uint64 213 // copies used by tracer 214 pcCopy uint64 // needed for the deferred Tracer 215 gasCopy uint64 // for Tracer to log gas remaining before execution 216 logged bool // deferred Tracer should ignore already logged steps 217 res []byte // result of the opcode execution function 218 ) 219 // Don't move this deferrred function, it's placed before the capturestate-deferred method, 220 // so that it get's executed _after_: the capturestate needs the stacks before 221 // they are returned to the pools 222 mem.Reset() 223 defer pool.Put(mem) 224 defer stack.ReturnNormalStack(locStack) 225 contract.Input = input 226 227 if in.cfg.Debug { 228 defer func() { 229 if err != nil { 230 if !logged { 231 in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.depth, err) //nolint:errcheck 232 } else { 233 in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.depth, err) 234 } 235 } 236 }() 237 } 238 // The Interpreter main run loop (contextual). This loop runs until either an 239 // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during 240 // the execution of one of the operations or until the done flag is set by the 241 // parent context. 242 steps := 0 243 for { 244 steps++ 245 if steps%1000 == 0 && in.evm.Cancelled() { 246 break 247 } 248 if in.cfg.Debug { 249 // Capture pre-execution values for tracing. 250 logged, pcCopy, gasCopy = false, _pc, contract.Gas 251 } 252 // Get the operation from the jump table and validate the stack to ensure there are 253 // enough stack items available to perform the operation. 254 op = contract.GetOp(_pc) 255 operation := in.jt[op] 256 cost = operation.constantGas // For tracing 257 // Validate stack 258 if sLen := locStack.Len(); sLen < operation.numPop { 259 return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.numPop} 260 } else if sLen > operation.maxStack { 261 return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} 262 } 263 if !contract.UseGas(cost) { 264 return nil, ErrOutOfGas 265 } 266 if operation.dynamicGas != nil { 267 // All ops with a dynamic memory usage also has a dynamic gas cost. 268 var memorySize uint64 269 // calculate the new memory size and expand the memory to fit 270 // the operation 271 // Memory check needs to be done prior to evaluating the dynamic gas portion, 272 // to detect calculation overflows 273 if operation.memorySize != nil { 274 memSize, overflow := operation.memorySize(locStack) 275 if overflow { 276 return nil, ErrGasUintOverflow 277 } 278 // memory is expanded in words of 32 bytes. Gas 279 // is also calculated in words. 280 if memorySize, overflow = math.SafeMul(ToWordSize(memSize), 32); overflow { 281 return nil, ErrGasUintOverflow 282 } 283 } 284 // Consume the gas and return an error if not enough gas is available. 285 // cost is explicitly set so that the capture state defer method can get the proper cost 286 var dynamicCost uint64 287 dynamicCost, err = operation.dynamicGas(in.evm, contract, locStack, mem, memorySize) 288 cost += dynamicCost // for tracing 289 if err != nil || !contract.UseGas(dynamicCost) { 290 return nil, ErrOutOfGas 291 } 292 if memorySize > 0 { 293 mem.Resize(memorySize) 294 } 295 } 296 if in.cfg.Debug { 297 in.cfg.Tracer.CaptureState(_pc, op, gasCopy, cost, callContext, in.returnData, in.depth, err) //nolint:errcheck 298 logged = true 299 } 300 // execute the operation 301 res, err = operation.execute(pc, in, callContext) 302 303 if err != nil { 304 break 305 } 306 _pc++ 307 } 308 309 if err == errStopToken { 310 err = nil // clear stop token error 311 } 312 313 ret = append(ret, res...) 314 return 315 } 316 317 // Depth returns the current call stack depth. 318 func (in *EVMInterpreter) Depth() int { 319 return in.depth 320 } 321 322 func (vm *VM) disableReadonly() { vm.readOnly = false } 323 func (vm *VM) noop() {} 324 325 func (vm *VM) setReadonly(outerReadonly bool) func() { 326 if outerReadonly && !vm.readOnly { 327 vm.readOnly = true 328 return func() { 329 vm.readOnly = false 330 } 331 } 332 return func() {} 333 } 334 335 func (vm *VM) getReadonly() bool { 336 return vm.readOnly 337 }