2015-07-07 00:54:22 +00:00
|
|
|
// Copyright 2014 The go-ethereum Authors
|
2015-07-22 16:48:40 +00:00
|
|
|
// This file is part of the go-ethereum library.
|
2015-07-07 00:54:22 +00:00
|
|
|
//
|
2015-07-23 16:35:11 +00:00
|
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
2015-07-07 00:54:22 +00: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 16:48:40 +00:00
|
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
2015-07-07 00:54:22 +00:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2015-07-22 16:48:40 +00:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2015-07-07 00:54:22 +00: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 16:48:40 +00:00
|
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
2015-07-07 00:54:22 +00:00
|
|
|
|
2014-10-18 11:31:20 +00:00
|
|
|
package vm
|
2014-07-22 09:54:48 +00:00
|
|
|
|
2015-01-19 10:18:34 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
2015-08-30 08:19:10 +00:00
|
|
|
"time"
|
2014-07-22 09:54:48 +00:00
|
|
|
|
2015-03-16 10:27:38 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2015-03-23 20:48:31 +00:00
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
2015-07-17 21:09:36 +00:00
|
|
|
"github.com/ethereum/go-ethereum/logger"
|
|
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
2015-04-02 03:17:15 +00:00
|
|
|
"github.com/ethereum/go-ethereum/params"
|
2015-01-19 10:18:34 +00:00
|
|
|
)
|
2014-10-15 15:12:26 +00:00
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
// Vm is an EVM and implements VirtualMachine
|
2014-07-22 09:54:48 +00:00
|
|
|
type Vm struct {
|
2015-01-19 10:18:34 +00:00
|
|
|
env Environment
|
|
|
|
}
|
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
// New returns a new Vm
|
2015-01-20 14:49:12 +00:00
|
|
|
func New(env Environment) *Vm {
|
2015-07-17 21:09:36 +00:00
|
|
|
return &Vm{env: env}
|
2015-01-19 10:18:34 +00:00
|
|
|
}
|
|
|
|
|
2015-06-10 15:40:13 +00:00
|
|
|
// Run loops and evaluates the contract's code with the given input data
|
2015-08-30 08:19:10 +00:00
|
|
|
func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
2015-01-19 10:18:34 +00:00
|
|
|
self.env.SetDepth(self.env.Depth() + 1)
|
2015-03-23 20:48:31 +00:00
|
|
|
defer self.env.SetDepth(self.env.Depth() - 1)
|
|
|
|
|
2015-07-17 21:09:36 +00:00
|
|
|
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
// In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
|
2015-08-30 08:19:10 +00:00
|
|
|
contract.UseGas(contract.Gas)
|
2015-07-17 21:09:36 +00:00
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
ret = contract.Return(nil)
|
2015-07-17 21:09:36 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
if contract.CodeAddr != nil {
|
|
|
|
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
|
|
|
|
return self.RunPrecompiled(p, input, contract)
|
2015-07-17 21:09:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
// Don't bother with the execution if there's no code.
|
|
|
|
if len(contract.Code) == 0 {
|
|
|
|
return contract.Return(nil), nil
|
|
|
|
}
|
|
|
|
|
2015-07-17 21:09:36 +00:00
|
|
|
var (
|
2015-08-30 08:19:10 +00:00
|
|
|
codehash = crypto.Sha3Hash(contract.Code) // codehash is used when doing jump dest caching
|
2015-07-17 21:09:36 +00:00
|
|
|
program *Program
|
|
|
|
)
|
2015-08-10 22:27:30 +00:00
|
|
|
if EnableJit {
|
2015-08-30 08:19:10 +00:00
|
|
|
// If the JIT is enabled check the status of the JIT program,
|
|
|
|
// if it doesn't exist compile a new program in a seperate
|
|
|
|
// goroutine or wait for compilation to finish if the JIT is
|
|
|
|
// forced.
|
|
|
|
switch GetProgramStatus(codehash) {
|
|
|
|
case progReady:
|
|
|
|
return RunProgram(GetProgram(codehash), self.env, contract, input)
|
|
|
|
case progUnknown:
|
2015-07-17 21:09:36 +00:00
|
|
|
if ForceJit {
|
|
|
|
// Create and compile program
|
2015-08-30 08:19:10 +00:00
|
|
|
program = NewProgram(contract.Code)
|
2015-07-17 21:09:36 +00:00
|
|
|
perr := CompileProgram(program)
|
|
|
|
if perr == nil {
|
2015-08-30 08:19:10 +00:00
|
|
|
return RunProgram(program, self.env, contract, input)
|
2015-07-17 21:09:36 +00:00
|
|
|
}
|
|
|
|
glog.V(logger.Info).Infoln("error compiling program", err)
|
|
|
|
} else {
|
|
|
|
// create and compile the program. Compilation
|
|
|
|
// is done in a seperate goroutine
|
2015-08-30 08:19:10 +00:00
|
|
|
program = NewProgram(contract.Code)
|
2015-07-17 21:09:36 +00:00
|
|
|
go func() {
|
|
|
|
err := CompileProgram(program)
|
|
|
|
if err != nil {
|
|
|
|
glog.V(logger.Info).Infoln("error compiling program", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-13 12:44:15 +00:00
|
|
|
var (
|
2015-08-30 08:19:10 +00:00
|
|
|
caller = contract.caller
|
|
|
|
code = contract.Code
|
|
|
|
instrCount = 0
|
|
|
|
|
|
|
|
op OpCode // current opcode
|
|
|
|
mem = NewMemory() // bound memory
|
|
|
|
stack = newstack() // local stack
|
|
|
|
statedb = self.env.Db() // current state
|
2015-06-12 11:35:14 +00:00
|
|
|
// For optimisation reason we're using uint64 as the program counter.
|
|
|
|
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible.
|
|
|
|
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 08:19:10 +00:00
|
|
|
if !contract.jumpdests.has(codehash, code, to) {
|
|
|
|
nop := contract.GetOp(to.Uint64())
|
2015-06-12 11:35:14 +00: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 12:44:15 +00:00
|
|
|
)
|
2015-08-30 08:19:10 +00:00
|
|
|
contract.Input = input
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-27 15:53:05 +00: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 15:09:57 +00:00
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
2015-08-30 08:19:10 +00:00
|
|
|
self.log(pc, op, contract.Gas, cost, mem, stack, contract, err)
|
2015-03-27 15:09:57 +00:00
|
|
|
}
|
|
|
|
}()
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-08-30 08:19:10 +00: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 12:58:57 +00:00
|
|
|
}
|
|
|
|
|
2015-08-30 08:19:10 +00: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")
|
|
|
|
return runProgram(program, pc, mem, stack, self.env, contract, input)
|
|
|
|
}
|
|
|
|
}
|
2015-07-17 21:09:36 +00:00
|
|
|
*/
|
2015-01-19 10:18:34 +00:00
|
|
|
|
|
|
|
// Get the memory location of pc
|
2015-08-30 08:19:10 +00:00
|
|
|
op = contract.GetOp(pc)
|
2015-06-10 15:40:13 +00:00
|
|
|
// calculate the new memory size and gas price for the current executing opcode
|
2015-08-30 08:19:10 +00:00
|
|
|
newMemSize, cost, err = calculateGasAndSize(self.env, contract, caller, op, statedb, mem, stack)
|
2015-03-27 15:09:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-06-10 15:40:13 +00:00
|
|
|
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
|
|
|
// Out Of Gas error
|
2015-08-30 08:19:10 +00:00
|
|
|
if !contract.UseGas(cost) {
|
2015-07-17 21:09:36 +00:00
|
|
|
return nil, OutOfGasError
|
2015-01-19 10:18:34 +00:00
|
|
|
}
|
2015-07-17 21:09:36 +00:00
|
|
|
|
2015-06-10 15:40:13 +00:00
|
|
|
// Resize the memory calculated previously
|
2015-01-19 10:18:34 +00:00
|
|
|
mem.Resize(newMemSize.Uint64())
|
2015-06-12 11:35:14 +00:00
|
|
|
// Add a log message
|
2015-08-30 08:19:10 +00:00
|
|
|
self.log(pc, op, contract.Gas, cost, mem, stack, contract, nil)
|
|
|
|
if opPtr := jumpTable[op]; opPtr.valid {
|
|
|
|
if opPtr.fn != nil {
|
|
|
|
opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack)
|
2015-01-19 10:18:34 +00:00
|
|
|
} else {
|
2015-08-30 08:19:10 +00:00
|
|
|
switch op {
|
|
|
|
case PC:
|
|
|
|
opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, self.env, contract, mem, stack)
|
|
|
|
case JUMP:
|
|
|
|
if err := jump(pc, stack.pop()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
continue
|
|
|
|
case JUMPI:
|
|
|
|
pos, cond := stack.pop(), stack.pop()
|
2015-03-16 17:42:18 +00:00
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
if cond.Cmp(common.BigTrue) >= 0 {
|
|
|
|
if err := jump(pc, pos); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-11-27 14:40:29 +00:00
|
|
|
case CREATE:
|
|
|
|
var (
|
|
|
|
value = stack.pop()
|
|
|
|
offset, size = stack.pop(), stack.pop()
|
|
|
|
input = mem.Get(offset.Int64(), size.Int64())
|
|
|
|
gas = new(big.Int).Set(contract.Gas)
|
|
|
|
addr common.Address
|
|
|
|
ret []byte
|
|
|
|
suberr error
|
|
|
|
)
|
|
|
|
contract.UseGas(contract.Gas)
|
|
|
|
ret, addr, suberr = self.env.Create(contract, input, gas, contract.Price, value)
|
|
|
|
if suberr != nil {
|
|
|
|
stack.push(new(big.Int))
|
|
|
|
} else {
|
|
|
|
// gas < len(ret) * Createinstr.dataGas == NO_CODE
|
|
|
|
dataGas := big.NewInt(int64(len(ret)))
|
|
|
|
dataGas.Mul(dataGas, params.CreateDataGas)
|
|
|
|
if contract.UseGas(dataGas) {
|
|
|
|
self.env.Db().SetCode(addr, ret)
|
|
|
|
} else {
|
|
|
|
if params.IsHomestead(self.env.BlockNumber()) {
|
|
|
|
stack.push(new(big.Int))
|
|
|
|
return nil, CodeStoreOutOfGasError
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stack.push(addr.Big())
|
|
|
|
}
|
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
case RETURN:
|
|
|
|
offset, size := stack.pop(), stack.pop()
|
|
|
|
ret := mem.GetPtr(offset.Int64(), size.Int64())
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
return contract.Return(ret), nil
|
|
|
|
case SUICIDE:
|
|
|
|
opSuicide(instruction{}, nil, self.env, contract, mem, stack)
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
fallthrough
|
|
|
|
case STOP: // Stop the contract
|
|
|
|
return contract.Return(nil), nil
|
2015-03-27 15:09:57 +00:00
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
}
|
2015-08-30 08:19:10 +00:00
|
|
|
} else {
|
2015-03-27 15:09:57 +00:00
|
|
|
return nil, fmt.Errorf("Invalid opcode %x", op)
|
2015-01-19 10:18:34 +00:00
|
|
|
}
|
|
|
|
|
2015-06-10 08:44:46 +00:00
|
|
|
pc++
|
2015-01-19 10:18:34 +00:00
|
|
|
|
|
|
|
}
|
2014-07-22 09:54:48 +00:00
|
|
|
}
|
|
|
|
|
2015-06-10 15:40:13 +00: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.
|
2015-08-30 08:19:10 +00:00
|
|
|
func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
|
2015-03-02 15:32:02 +00:00
|
|
|
var (
|
|
|
|
gas = new(big.Int)
|
|
|
|
newMemSize *big.Int = new(big.Int)
|
|
|
|
)
|
2015-03-27 15:09:57 +00:00
|
|
|
err := baseCheck(op, stack, gas)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-09 23:25:27 +00:00
|
|
|
// stack Check, memory resize & gas phase
|
2015-01-19 10:18:34 +00:00
|
|
|
switch op {
|
|
|
|
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 15:09:57 +00:00
|
|
|
err := stack.require(n)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2015-03-03 10:11:11 +00:00
|
|
|
gas.Set(GasFastestStep)
|
2015-01-19 10:18:34 +00: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 15:09:57 +00:00
|
|
|
err := stack.require(n)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2015-03-03 10:11:11 +00:00
|
|
|
gas.Set(GasFastestStep)
|
2015-01-19 10:18:34 +00:00
|
|
|
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
|
|
|
n := int(op - LOG0)
|
2015-03-27 15:09:57 +00:00
|
|
|
err := stack.require(n + 2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-09 23:25:27 +00:00
|
|
|
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
2015-03-02 15:32:02 +00:00
|
|
|
|
2015-04-02 03:17:15 +00: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 10:18:34 +00:00
|
|
|
|
|
|
|
newMemSize = calcMemSize(mStart, mSize)
|
|
|
|
case EXP:
|
2015-04-02 03:17:15 +00:00
|
|
|
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
|
2015-01-19 10:18:34 +00:00
|
|
|
case SSTORE:
|
2015-03-27 15:09:57 +00:00
|
|
|
err := stack.require(2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-02 15:32:02 +00:00
|
|
|
var g *big.Int
|
2015-03-09 23:25:27 +00:00
|
|
|
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
2015-08-30 08:19:10 +00:00
|
|
|
val := statedb.GetState(contract.Address(), common.BigToHash(x))
|
2015-06-18 10:25:02 +00: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)
|
|
|
|
// 3. From a nen-zero to a non-zero (CHANGE)
|
2015-06-17 09:24:40 +00:00
|
|
|
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
2015-01-19 10:18:34 +00:00
|
|
|
// 0 => non 0
|
2015-04-02 03:17:15 +00:00
|
|
|
g = params.SstoreSetGas
|
2015-06-17 09:24:40 +00:00
|
|
|
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
2015-08-30 08:19:10 +00:00
|
|
|
statedb.AddRefund(params.SstoreRefundGas)
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-04-02 03:17:15 +00:00
|
|
|
g = params.SstoreClearGas
|
2015-01-19 10:18:34 +00:00
|
|
|
} else {
|
|
|
|
// non 0 => non 0 (or 0 => 0)
|
2015-04-02 03:17:15 +00:00
|
|
|
g = params.SstoreClearGas
|
2015-01-19 10:18:34 +00:00
|
|
|
}
|
2015-03-02 15:32:02 +00:00
|
|
|
gas.Set(g)
|
2015-03-09 10:28:35 +00:00
|
|
|
case SUICIDE:
|
2015-08-30 08:19:10 +00:00
|
|
|
if !statedb.IsDeleted(contract.Address()) {
|
|
|
|
statedb.AddRefund(params.SuicideRefundGas)
|
2015-03-09 10:28:35 +00:00
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
case MLOAD:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.peek(), u256(32))
|
2015-01-19 10:18:34 +00:00
|
|
|
case MSTORE8:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.peek(), u256(1))
|
2015-03-02 16:55:45 +00:00
|
|
|
case MSTORE:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.peek(), u256(32))
|
2015-01-19 10:18:34 +00:00
|
|
|
case RETURN:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
2015-01-19 10:18:34 +00:00
|
|
|
case SHA3:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-09 23:25:27 +00:00
|
|
|
words := toWordSize(stack.data[stack.len()-2])
|
2015-04-02 03:17:15 +00:00
|
|
|
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
2015-03-02 15:32:02 +00:00
|
|
|
case CALLDATACOPY:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-09 23:25:27 +00:00
|
|
|
words := toWordSize(stack.data[stack.len()-3])
|
2015-04-02 03:17:15 +00:00
|
|
|
gas.Add(gas, words.Mul(words, params.CopyGas))
|
2015-03-02 15:32:02 +00:00
|
|
|
case CODECOPY:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-09 23:25:27 +00:00
|
|
|
words := toWordSize(stack.data[stack.len()-3])
|
2015-04-02 03:17:15 +00:00
|
|
|
gas.Add(gas, words.Mul(words, params.CopyGas))
|
2015-03-02 15:32:02 +00:00
|
|
|
case EXTCODECOPY:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
2015-03-02 15:32:02 +00:00
|
|
|
|
2015-03-09 23:25:27 +00:00
|
|
|
words := toWordSize(stack.data[stack.len()-4])
|
2015-04-02 03:17:15 +00:00
|
|
|
gas.Add(gas, words.Mul(words, params.CopyGas))
|
2015-03-03 12:29:52 +00:00
|
|
|
|
2015-03-02 15:32:02 +00:00
|
|
|
case CREATE:
|
2015-03-09 23:25:27 +00:00
|
|
|
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
2015-01-19 10:18:34 +00:00
|
|
|
case CALL, CALLCODE:
|
2015-03-09 23:25:27 +00:00
|
|
|
gas.Add(gas, stack.data[stack.len()-1])
|
2015-03-02 15:32:02 +00:00
|
|
|
|
|
|
|
if op == CALL {
|
2015-08-30 08:19:10 +00:00
|
|
|
//if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
|
|
|
|
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
2015-04-02 03:17:15 +00:00
|
|
|
gas.Add(gas, params.CallNewAccountGas)
|
2015-03-02 15:32:02 +00:00
|
|
|
}
|
2015-03-03 12:29:52 +00:00
|
|
|
}
|
2015-03-02 15:32:02 +00:00
|
|
|
|
2015-03-09 23:25:27 +00:00
|
|
|
if len(stack.data[stack.len()-3].Bytes()) > 0 {
|
2015-04-02 03:17:15 +00:00
|
|
|
gas.Add(gas, params.CallValueTransferGas)
|
2015-03-02 15:32:02 +00:00
|
|
|
}
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-09 23:25:27 +00: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 10:18:34 +00:00
|
|
|
|
2015-11-27 14:40:29 +00:00
|
|
|
newMemSize = common.BigMax(x, y)
|
|
|
|
case DELEGATECALL:
|
|
|
|
gas.Add(gas, stack.data[stack.len()-1])
|
|
|
|
|
|
|
|
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 10:27:38 +00:00
|
|
|
newMemSize = common.BigMax(x, y)
|
2015-01-19 10:18:34 +00:00
|
|
|
}
|
2015-08-30 08:19:10 +00:00
|
|
|
quadMemGas(mem, newMemSize, gas)
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-03-27 15:09:57 +00:00
|
|
|
return newMemSize, gas, nil
|
2014-09-14 23:11:01 +00:00
|
|
|
}
|
|
|
|
|
2015-06-10 15:40:13 +00:00
|
|
|
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
|
2015-08-30 08:19:10 +00:00
|
|
|
func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
|
2015-06-10 15:40:13 +00:00
|
|
|
gas := p.Gas(len(input))
|
2015-08-30 08:19:10 +00:00
|
|
|
if contract.UseGas(gas) {
|
2015-06-10 15:40:13 +00:00
|
|
|
ret = p.Call(input)
|
2015-01-19 10:18:34 +00:00
|
|
|
|
2015-08-30 08:19:10 +00:00
|
|
|
return contract.Return(ret), nil
|
2015-01-19 10:18:34 +00:00
|
|
|
} else {
|
2015-07-03 17:54:14 +00:00
|
|
|
return nil, OutOfGasError
|
2015-01-19 10:18:34 +00:00
|
|
|
}
|
2014-07-22 09:54:48 +00:00
|
|
|
}
|
|
|
|
|
2015-06-10 15:40:13 +00:00
|
|
|
// log emits a log event to the environment for each opcode encountered. This is not to be confused with the
|
|
|
|
// LOG* opcode.
|
2015-08-30 08:19:10 +00:00
|
|
|
func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) {
|
2016-01-28 18:21:05 +00:00
|
|
|
if Debug || GenerateStructLogs {
|
2015-06-10 10:23:49 +00:00
|
|
|
mem := make([]byte, len(memory.Data()))
|
|
|
|
copy(mem, memory.Data())
|
2015-06-10 10:57:37 +00:00
|
|
|
|
2015-10-11 21:14:35 +00:00
|
|
|
stck := make([]*big.Int, len(stack.Data()))
|
|
|
|
for i, item := range stack.Data() {
|
|
|
|
stck[i] = new(big.Int).Set(item)
|
|
|
|
}
|
2015-06-10 10:57:37 +00:00
|
|
|
storage := make(map[common.Hash][]byte)
|
2016-01-11 16:20:31 +00:00
|
|
|
contract.self.EachStorage(func(k, v []byte) {
|
|
|
|
storage[common.BytesToHash(k)] = v
|
|
|
|
})
|
2015-06-12 11:35:14 +00:00
|
|
|
self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, err})
|
2015-01-19 10:18:34 +00:00
|
|
|
}
|
2014-09-19 11:19:19 +00:00
|
|
|
}
|
2014-10-14 11:37:26 +00:00
|
|
|
|
2015-06-10 15:40:13 +00:00
|
|
|
// Environment returns the current workable state of the VM
|
2015-01-19 10:18:34 +00:00
|
|
|
func (self *Vm) Env() Environment {
|
|
|
|
return self.env
|
|
|
|
}
|