| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2014 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-18 13:31:20 +02:00
										 |  |  | package vm | 
					
						
							| 
									
										
										
										
											2014-07-22 11:54:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2014-07-22 11:54:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-16 11:27:38 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-03-23 21:48:31 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/logger" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger/glog" | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/params" | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2014-10-15 17:12:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | // Config are the configuration options for the EVM | 
					
						
							|  |  |  | type Config struct { | 
					
						
							|  |  |  | 	Debug     bool | 
					
						
							|  |  |  | 	EnableJit bool | 
					
						
							|  |  |  | 	ForceJit  bool | 
					
						
							| 
									
										
										
										
											2016-08-19 15:19:51 +01:00
										 |  |  | 	Tracer    Tracer | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // EVM is used to run Ethereum based contracts and will utilise the | 
					
						
							|  |  |  | // passed environment to query external sources for state information. | 
					
						
							|  |  |  | // The EVM will run the byte code VM or JIT VM based on the passed | 
					
						
							|  |  |  | // configuration. | 
					
						
							|  |  |  | type EVM struct { | 
					
						
							| 
									
										
										
										
											2016-01-21 15:29:58 +01:00
										 |  |  | 	env       Environment | 
					
						
							|  |  |  | 	jumpTable vmJumpTable | 
					
						
							| 
									
										
										
										
											2016-03-01 23:32:43 +01:00
										 |  |  | 	cfg       Config | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 	gasTable  params.GasTable | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | // New returns a new instance of the EVM. | 
					
						
							| 
									
										
										
										
											2016-03-01 23:32:43 +01:00
										 |  |  | func New(env Environment, cfg Config) *EVM { | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 	return &EVM{ | 
					
						
							|  |  |  | 		env:       env, | 
					
						
							| 
									
										
										
										
											2016-03-01 23:32:43 +01:00
										 |  |  | 		jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()), | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 		cfg:       cfg, | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		gasTable:  env.RuleSet().GasTable(env.BlockNumber()), | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-10 17:40:13 +02:00
										 |  |  | // Run loops and evaluates the contract's code with the given input data | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { | 
					
						
							|  |  |  | 	evm.env.SetDepth(evm.env.Depth() + 1) | 
					
						
							|  |  |  | 	defer evm.env.SetDepth(evm.env.Depth() - 1) | 
					
						
							| 
									
										
										
										
											2015-03-23 21:48:31 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 	if contract.CodeAddr != nil { | 
					
						
							|  |  |  | 		if p := Precompiled[contract.CodeAddr.Str()]; p != nil { | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 			return evm.RunPrecompiled(p, input, contract) | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 	// Don't bother with the execution if there's no code. | 
					
						
							|  |  |  | 	if len(contract.Code) == 0 { | 
					
						
							| 
									
										
										
										
											2016-01-19 23:50:00 +01:00
										 |  |  | 		return nil, nil | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-01 15:44:53 +03:00
										 |  |  | 	codehash := contract.CodeHash // codehash is used when doing jump dest caching | 
					
						
							|  |  |  | 	if codehash == (common.Hash{}) { | 
					
						
							|  |  |  | 		codehash = crypto.Keccak256Hash(contract.Code) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var program *Program | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 	if evm.cfg.EnableJit { | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 		// If the JIT is enabled check the status of the JIT program, | 
					
						
							| 
									
										
										
										
											2016-03-15 11:08:18 -07:00
										 |  |  | 		// if it doesn't exist compile a new program in a separate | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 		// goroutine or wait for compilation to finish if the JIT is | 
					
						
							|  |  |  | 		// forced. | 
					
						
							|  |  |  | 		switch GetProgramStatus(codehash) { | 
					
						
							|  |  |  | 		case progReady: | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 			return RunProgram(GetProgram(codehash), evm.env, contract, input) | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 		case progUnknown: | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 			if evm.cfg.ForceJit { | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 				// Create and compile program | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 				program = NewProgram(contract.Code) | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 				perr := CompileProgram(program) | 
					
						
							|  |  |  | 				if perr == nil { | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 					return RunProgram(program, evm.env, contract, input) | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				glog.V(logger.Info).Infoln("error compiling program", err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// create and compile the program. Compilation | 
					
						
							| 
									
										
										
										
											2016-03-15 11:08:18 -07:00
										 |  |  | 				// is done in a separate goroutine | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 				program = NewProgram(contract.Code) | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 				go func() { | 
					
						
							|  |  |  | 					err := CompileProgram(program) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						glog.V(logger.Info).Infoln("error compiling program", err) | 
					
						
							|  |  |  | 						return | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-13 13:44:15 +01:00
										 |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 		caller     = contract.caller | 
					
						
							|  |  |  | 		code       = contract.Code | 
					
						
							|  |  |  | 		instrCount = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 		op      OpCode         // current opcode | 
					
						
							|  |  |  | 		mem     = NewMemory()  // bound memory | 
					
						
							|  |  |  | 		stack   = newstack()   // local stack | 
					
						
							|  |  |  | 		statedb = evm.env.Db() // current state | 
					
						
							| 
									
										
										
										
											2015-06-12 13:35:14 +02:00
										 |  |  | 		// For optimisation reason we're using uint64 as the program counter. | 
					
						
							| 
									
										
										
										
											2016-03-15 11:08:18 -07:00
										 |  |  | 		// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible. | 
					
						
							| 
									
										
										
										
											2015-06-12 13:35:14 +02:00
										 |  |  | 		pc = uint64(0) // program counter | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// jump evaluates and checks whether the given jump destination is a valid one | 
					
						
							|  |  |  | 		// if valid move the `pc` otherwise return an error. | 
					
						
							|  |  |  | 		jump = func(from uint64, to *big.Int) error { | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 			if !contract.jumpdests.has(codehash, code, to) { | 
					
						
							|  |  |  | 				nop := contract.GetOp(to.Uint64()) | 
					
						
							| 
									
										
										
										
											2015-06-12 13:35:14 +02:00
										 |  |  | 				return fmt.Errorf("invalid jump destination (%v) %v", nop, to) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			pc = to.Uint64() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		newMemSize *big.Int | 
					
						
							|  |  |  | 		cost       *big.Int | 
					
						
							| 
									
										
										
										
											2015-03-13 13:44:15 +01:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 	contract.Input = input | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-27 16:53:05 +01:00
										 |  |  | 	// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 	defer func() { | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 		if err != nil && evm.cfg.Debug { | 
					
						
							| 
									
										
										
										
											2016-08-19 15:19:51 +01:00
										 |  |  | 			evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err) | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 	if glog.V(logger.Debug) { | 
					
						
							|  |  |  | 		glog.Infof("running byte VM %x\n", codehash[:4]) | 
					
						
							|  |  |  | 		tstart := time.Now() | 
					
						
							|  |  |  | 		defer func() { | 
					
						
							|  |  |  | 			glog.Infof("byte VM %x done. time: %v instrc: %v\n", codehash[:4], time.Since(tstart), instrCount) | 
					
						
							|  |  |  | 		}() | 
					
						
							| 
									
										
										
										
											2015-05-29 14:58:57 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 	for ; ; instrCount++ { | 
					
						
							|  |  |  | 		/* | 
					
						
							|  |  |  | 			if EnableJit && it%100 == 0 { | 
					
						
							|  |  |  | 				if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady { | 
					
						
							|  |  |  | 					// move execution | 
					
						
							|  |  |  | 					fmt.Println("moved", it) | 
					
						
							|  |  |  | 					glog.V(logger.Info).Infoln("Moved execution to JIT") | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 					return runProgram(program, pc, mem, stack, evm.env, contract, input) | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 		*/ | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Get the memory location of pc | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 		op = contract.GetOp(pc) | 
					
						
							| 
									
										
										
										
											2015-06-10 17:40:13 +02:00
										 |  |  | 		// calculate the new memory size and gas price for the current executing opcode | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack) | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-10 17:40:13 +02:00
										 |  |  | 		// Use the calculated gas. When insufficient gas is present, use all gas and return an | 
					
						
							|  |  |  | 		// Out Of Gas error | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 		if !contract.UseGas(cost) { | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 			return nil, OutOfGasError | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-07-17 23:09:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-10 17:40:13 +02:00
										 |  |  | 		// Resize the memory calculated previously | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 		mem.Resize(newMemSize.Uint64()) | 
					
						
							| 
									
										
										
										
											2015-06-12 13:35:14 +02:00
										 |  |  | 		// Add a log message | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 		if evm.cfg.Debug { | 
					
						
							| 
									
										
										
										
											2016-08-19 15:19:51 +01:00
										 |  |  | 			evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil) | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if opPtr := evm.jumpTable[op]; opPtr.valid { | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 			if opPtr.fn != nil { | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 				opPtr.fn(instruction{}, &pc, evm.env, contract, mem, stack) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 				switch op { | 
					
						
							|  |  |  | 				case PC: | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 					opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, evm.env, contract, mem, stack) | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 				case JUMP: | 
					
						
							|  |  |  | 					if err := jump(pc, stack.pop()); err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				case JUMPI: | 
					
						
							|  |  |  | 					pos, cond := stack.pop(), stack.pop() | 
					
						
							| 
									
										
										
										
											2015-03-16 18:42:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 					if cond.Cmp(common.BigTrue) >= 0 { | 
					
						
							|  |  |  | 						if err := jump(pc, pos); err != nil { | 
					
						
							|  |  |  | 							return nil, err | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				case RETURN: | 
					
						
							|  |  |  | 					offset, size := stack.pop(), stack.pop() | 
					
						
							|  |  |  | 					ret := mem.GetPtr(offset.Int64(), size.Int64()) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-19 23:50:00 +01:00
										 |  |  | 					return ret, nil | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 				case SUICIDE: | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | 					opSuicide(instruction{}, nil, evm.env, contract, mem, stack) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 					fallthrough | 
					
						
							|  |  |  | 				case STOP: // Stop the contract | 
					
						
							| 
									
										
										
										
											2016-01-19 23:50:00 +01:00
										 |  |  | 					return nil, nil | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 			return nil, fmt.Errorf("Invalid opcode %x", op) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-10 10:44:46 +02:00
										 |  |  | 		pc++ | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-07-22 11:54:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-10 17:40:13 +02:00
										 |  |  | // calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for | 
					
						
							|  |  |  | // the operation. This does not reduce gas or resizes the memory. | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		gas                 = new(big.Int) | 
					
						
							|  |  |  | 		newMemSize *big.Int = new(big.Int) | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 	err := baseCheck(op, stack, gas) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 	// stack Check, memory resize & gas phase | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	switch op { | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 	case SUICIDE: | 
					
						
							|  |  |  | 		// if suicide is not nil: homestead gas fork | 
					
						
							|  |  |  | 		if gasTable.CreateBySuicide != nil { | 
					
						
							|  |  |  | 			gas.Set(gasTable.Suicide) | 
					
						
							|  |  |  | 			if !env.Db().Exist(common.BigToAddress(stack.data[len(stack.data)-1])) { | 
					
						
							|  |  |  | 				gas.Add(gas, gasTable.CreateBySuicide) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if !statedb.HasSuicided(contract.Address()) { | 
					
						
							|  |  |  | 			statedb.AddRefund(params.SuicideRefundGas) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case EXTCODESIZE: | 
					
						
							|  |  |  | 		gas.Set(gasTable.ExtcodeSize) | 
					
						
							|  |  |  | 	case BALANCE: | 
					
						
							|  |  |  | 		gas.Set(gasTable.Balance) | 
					
						
							|  |  |  | 	case SLOAD: | 
					
						
							|  |  |  | 		gas.Set(gasTable.SLoad) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: | 
					
						
							|  |  |  | 		n := int(op - SWAP1 + 2) | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 		err := stack.require(n) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-03 11:11:11 +01:00
										 |  |  | 		gas.Set(GasFastestStep) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: | 
					
						
							|  |  |  | 		n := int(op - DUP1 + 1) | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 		err := stack.require(n) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-03 11:11:11 +01:00
										 |  |  | 		gas.Set(GasFastestStep) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case LOG0, LOG1, LOG2, LOG3, LOG4: | 
					
						
							|  |  |  | 		n := int(op - LOG0) | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 		err := stack.require(n + 2) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1] | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 		gas.Add(gas, params.LogGas) | 
					
						
							|  |  |  | 		gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas)) | 
					
						
							|  |  |  | 		gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas)) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		newMemSize = calcMemSize(mStart, mSize) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case EXP: | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 		gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case SSTORE: | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 		err := stack.require(2) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 		var g *big.Int | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 		val := statedb.GetState(contract.Address(), common.BigToHash(x)) | 
					
						
							| 
									
										
										
										
											2015-06-18 12:25:02 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// This checks for 3 scenario's and calculates gas accordingly | 
					
						
							|  |  |  | 		// 1. From a zero-value address to a non-zero value         (NEW VALUE) | 
					
						
							|  |  |  | 		// 2. From a non-zero value address to a zero-value address (DELETE) | 
					
						
							| 
									
										
										
										
											2016-03-15 11:08:18 -07:00
										 |  |  | 		// 3. From a non-zero to a non-zero                         (CHANGE) | 
					
						
							| 
									
										
										
										
											2015-06-17 11:24:40 +02:00
										 |  |  | 		if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 			// 0 => non 0 | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 			g = params.SstoreSetGas | 
					
						
							| 
									
										
										
										
											2015-06-17 11:24:40 +02:00
										 |  |  | 		} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 			statedb.AddRefund(params.SstoreRefundGas) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 			g = params.SstoreClearGas | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			// non 0 => non 0 (or 0 => 0) | 
					
						
							| 
									
										
										
										
											2016-06-21 21:03:06 +02:00
										 |  |  | 			g = params.SstoreResetGas | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 		gas.Set(g) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case MLOAD: | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.peek(), u256(32)) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case MSTORE8: | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.peek(), u256(1)) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-03-02 17:55:45 +01:00
										 |  |  | 	case MSTORE: | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.peek(), u256(32)) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case RETURN: | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case SHA3: | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		words := toWordSize(stack.data[stack.len()-2]) | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 		gas.Add(gas, words.Mul(words, params.Sha3WordGas)) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 	case CALLDATACOPY: | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		words := toWordSize(stack.data[stack.len()-3]) | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 		gas.Add(gas, words.Mul(words, params.CopyGas)) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 	case CODECOPY: | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		words := toWordSize(stack.data[stack.len()-3]) | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 		gas.Add(gas, words.Mul(words, params.CopyGas)) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 	case EXTCODECOPY: | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		gas.Set(gasTable.ExtcodeCopy) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		words := toWordSize(stack.data[stack.len()-4]) | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 		gas.Add(gas, words.Mul(words, params.CopyGas)) | 
					
						
							| 
									
										
										
										
											2015-03-03 13:29:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 	case CREATE: | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	case CALL, CALLCODE: | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		gas.Set(gasTable.Calls) | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if op == CALL { | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 			if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 				gas.Add(gas, params.CallNewAccountGas) | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-03-03 13:29:52 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		if len(stack.data[stack.len()-3].Bytes()) > 0 { | 
					
						
							| 
									
										
										
										
											2015-04-02 05:17:15 +02:00
										 |  |  | 			gas.Add(gas, params.CallValueTransferGas) | 
					
						
							| 
									
										
										
										
											2015-03-02 16:32:02 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-10 00:25:27 +01:00
										 |  |  | 		x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) | 
					
						
							|  |  |  | 		y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-27 15:40:29 +01:00
										 |  |  | 		newMemSize = common.BigMax(x, y) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1]) | 
					
						
							|  |  |  | 		// Replace the stack item with the new gas calculation. This means that | 
					
						
							|  |  |  | 		// either the original item is left on the stack or the item is replaced by: | 
					
						
							|  |  |  | 		// (availableGas - gas) * 63 / 64 | 
					
						
							|  |  |  | 		// We replace the stack item so that it's available when the opCall instruction is | 
					
						
							|  |  |  | 		// called. This information is otherwise lost due to the dependency on *current* | 
					
						
							|  |  |  | 		// available gas. | 
					
						
							|  |  |  | 		stack.data[stack.len()-1] = cg | 
					
						
							|  |  |  | 		gas.Add(gas, cg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-27 15:40:29 +01:00
										 |  |  | 	case DELEGATECALL: | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 		gas.Set(gasTable.Calls) | 
					
						
							| 
									
										
										
										
											2015-11-27 15:40:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6]) | 
					
						
							|  |  |  | 		y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-16 11:27:38 +01:00
										 |  |  | 		newMemSize = common.BigMax(x, y) | 
					
						
							| 
									
										
										
										
											2016-10-08 00:23:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		quadMemGas(mem, newMemSize, gas) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1]) | 
					
						
							|  |  |  | 		// Replace the stack item with the new gas calculation. This means that | 
					
						
							|  |  |  | 		// either the original item is left on the stack or the item is replaced by: | 
					
						
							|  |  |  | 		// (availableGas - gas) * 63 / 64 | 
					
						
							|  |  |  | 		// We replace the stack item so that it's available when the opCall instruction is | 
					
						
							|  |  |  | 		// called. | 
					
						
							|  |  |  | 		stack.data[stack.len()-1] = cg | 
					
						
							|  |  |  | 		gas.Add(gas, cg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-27 16:09:57 +01:00
										 |  |  | 	return newMemSize, gas, nil | 
					
						
							| 
									
										
										
										
											2014-09-15 01:11:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-10 17:40:13 +02:00
										 |  |  | // RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go | 
					
						
							| 
									
										
										
										
											2016-02-03 23:46:27 +01:00
										 |  |  | func (evm *EVM) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) { | 
					
						
							| 
									
										
										
										
											2015-06-10 17:40:13 +02:00
										 |  |  | 	gas := p.Gas(len(input)) | 
					
						
							| 
									
										
										
										
											2015-08-30 10:19:10 +02:00
										 |  |  | 	if contract.UseGas(gas) { | 
					
						
							| 
									
										
										
										
											2015-06-10 17:40:13 +02:00
										 |  |  | 		ret = p.Call(input) | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-19 23:50:00 +01:00
										 |  |  | 		return ret, nil | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2015-07-03 19:54:14 +02:00
										 |  |  | 		return nil, OutOfGasError | 
					
						
							| 
									
										
										
										
											2015-01-19 11:18:34 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-07-22 11:54:48 +02:00
										 |  |  | } |