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 &copy
   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  }