aboutsummaryrefslogtreecommitdiffstats
path: root/core/vm
diff options
context:
space:
mode:
authorJeffrey Wilcke <geffobscura@gmail.com>2015-11-03 18:47:07 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2015-11-03 18:47:07 +0800
commite5532154a50114d5ffb1ffd850b746cab00cb899 (patch)
tree0042cc997ccf4166b9b52464339d52d37d7a8ad6 /core/vm
parent9666db2a442887ccf8ec2d81f5e2fedc1a3a3d3e (diff)
parentf75becc264f8bde0f58391fc226243d03e78aa7b (diff)
downloadgo-tangerine-e5532154a50114d5ffb1ffd850b746cab00cb899.tar
go-tangerine-e5532154a50114d5ffb1ffd850b746cab00cb899.tar.gz
go-tangerine-e5532154a50114d5ffb1ffd850b746cab00cb899.tar.bz2
go-tangerine-e5532154a50114d5ffb1ffd850b746cab00cb899.tar.lz
go-tangerine-e5532154a50114d5ffb1ffd850b746cab00cb899.tar.xz
go-tangerine-e5532154a50114d5ffb1ffd850b746cab00cb899.tar.zst
go-tangerine-e5532154a50114d5ffb1ffd850b746cab00cb899.zip
Merge branch 'release/1.3.0'
Conflicts: VERSION cmd/geth/main.go
Diffstat (limited to 'core/vm')
-rw-r--r--core/vm/asm.go2
-rw-r--r--core/vm/common.go55
-rw-r--r--core/vm/contract.go (renamed from core/vm/context.go)52
-rw-r--r--core/vm/contracts.go10
-rw-r--r--core/vm/doc.go35
-rw-r--r--core/vm/environment.go87
-rw-r--r--core/vm/gas.go2
-rw-r--r--core/vm/instructions.go323
-rw-r--r--core/vm/jit.go167
-rw-r--r--core/vm/jit_optimiser.go107
-rw-r--r--core/vm/jit_test.go139
-rw-r--r--core/vm/jit_util.go68
-rw-r--r--core/vm/jit_util_test.go84
-rw-r--r--core/vm/jump_table.go143
-rw-r--r--core/vm/log.go71
-rw-r--r--core/vm/logger.go1
-rw-r--r--core/vm/memory.go7
-rw-r--r--core/vm/opcodes.go28
-rw-r--r--core/vm/segments.go44
-rw-r--r--core/vm/settings.go25
-rw-r--r--core/vm/stack.go3
-rw-r--r--core/vm/virtual_machine.go3
-rw-r--r--core/vm/vm.go690
-rw-r--r--core/vm/vm_jit.go3
24 files changed, 1237 insertions, 912 deletions
diff --git a/core/vm/asm.go b/core/vm/asm.go
index 639201e50..065d3eb97 100644
--- a/core/vm/asm.go
+++ b/core/vm/asm.go
@@ -23,6 +23,8 @@ import (
"github.com/ethereum/go-ethereum/common"
)
+// Dissassemble dissassembles the byte code and returns the string
+// representation (human readable opcodes).
func Disassemble(script []byte) (asm []string) {
pc := new(big.Int)
for {
diff --git a/core/vm/common.go b/core/vm/common.go
index 2e03ec80b..2d1aa9332 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -22,34 +22,34 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/params"
)
// Global Debug flag indicating Debug VM (full logging)
var Debug bool
+// Type is the VM type accepted by **NewVm**
type Type byte
const (
- StdVmTy Type = iota
- JitVmTy
+ StdVmTy Type = iota // Default standard VM
+ JitVmTy // LLVM JIT VM
MaxVmTy
-
- LogTyPretty byte = 0x1
- LogTyDiff byte = 0x2
)
var (
- Pow256 = common.BigPow(2, 256)
+ Pow256 = common.BigPow(2, 256) // Pow256 is 2**256
- U256 = common.U256
- S256 = common.S256
+ U256 = common.U256 // Shortcut to common.U256
+ S256 = common.S256 // Shortcut to common.S256
- Zero = common.Big0
- One = common.Big1
+ Zero = common.Big0 // Shortcut to common.Big0
+ One = common.Big1 // Shortcut to common.Big1
- max = big.NewInt(math.MaxInt64)
+ max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer
)
+// NewVm returns a new VM based on the Environment
func NewVm(env Environment) VirtualMachine {
switch env.VmType() {
case JitVmTy:
@@ -62,6 +62,7 @@ func NewVm(env Environment) VirtualMachine {
}
}
+// calculates the memory size required for a step
func calcMemSize(off, l *big.Int) *big.Int {
if l.Cmp(common.Big0) == 0 {
return common.Big0
@@ -70,6 +71,32 @@ func calcMemSize(off, l *big.Int) *big.Int {
return new(big.Int).Add(off, l)
}
+// calculates the quadratic gas
+func quadMemGas(mem *Memory, newMemSize, gas *big.Int) {
+ if newMemSize.Cmp(common.Big0) > 0 {
+ newMemSizeWords := toWordSize(newMemSize)
+ newMemSize.Mul(newMemSizeWords, u256(32))
+
+ if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
+ // be careful reusing variables here when changing.
+ // The order has been optimised to reduce allocation
+ oldSize := toWordSize(big.NewInt(int64(mem.Len())))
+ pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
+ linCoef := oldSize.Mul(oldSize, params.MemoryGas)
+ quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
+ oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
+
+ pow.Exp(newMemSizeWords, common.Big2, Zero)
+ linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
+ quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
+ newTotalFee := linCoef.Add(linCoef, quadCoef)
+
+ fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
+ gas.Add(gas, fee)
+ }
+ }
+}
+
// Simple helper
func u256(n int64) *big.Int {
return big.NewInt(n)
@@ -86,6 +113,8 @@ func toValue(val *big.Int) interface{} {
return val
}
+// getData returns a slice from the data based on the start and size and pads
+// up to size with zero's. This function is overflow safe.
func getData(data []byte, start, size *big.Int) []byte {
dlen := big.NewInt(int64(len(data)))
@@ -94,7 +123,9 @@ func getData(data []byte, start, size *big.Int) []byte {
return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
}
-func UseGas(gas, amount *big.Int) bool {
+// useGas attempts to subtract the amount of gas and returns whether it was
+// successful
+func useGas(gas, amount *big.Int) bool {
if gas.Cmp(amount) < 0 {
return false
}
diff --git a/core/vm/context.go b/core/vm/contract.go
index d17934ba5..95417e747 100644
--- a/core/vm/context.go
+++ b/core/vm/contract.go
@@ -22,15 +22,18 @@ import (
"github.com/ethereum/go-ethereum/common"
)
-type ContextRef interface {
+// ContractRef is a reference to the contract's backing object
+type ContractRef interface {
ReturnGas(*big.Int, *big.Int)
Address() common.Address
SetCode([]byte)
}
-type Context struct {
- caller ContextRef
- self ContextRef
+// Contract represents an ethereum contract in the state database. It contains
+// the the contract code, calling arguments. Contract implements ContractReg
+type Contract struct {
+ caller ContractRef
+ self ContractRef
jumpdests destinations // result of JUMPDEST analysis.
@@ -44,10 +47,10 @@ type Context struct {
}
// Create a new context for the given data items.
-func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context {
- c := &Context{caller: caller, self: object, Args: nil}
+func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
+ c := &Contract{caller: caller, self: object, Args: nil}
- if parent, ok := caller.(*Context); ok {
+ if parent, ok := caller.(*Contract); ok {
// Reuse JUMPDEST analysis from parent context if available.
c.jumpdests = parent.jumpdests
} else {
@@ -66,11 +69,13 @@ func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int
return c
}
-func (c *Context) GetOp(n uint64) OpCode {
+// GetOp returns the n'th element in the contract's byte array
+func (c *Contract) GetOp(n uint64) OpCode {
return OpCode(c.GetByte(n))
}
-func (c *Context) GetByte(n uint64) byte {
+// GetByte returns the n'th byte in the contract's byte array
+func (c *Contract) GetByte(n uint64) byte {
if n < uint64(len(c.Code)) {
return c.Code[n]
}
@@ -78,43 +83,44 @@ func (c *Context) GetByte(n uint64) byte {
return 0
}
-func (c *Context) Return(ret []byte) []byte {
+// Return returns the given ret argument and returns any remaining gas to the
+// caller
+func (c *Contract) Return(ret []byte) []byte {
// Return the remaining gas to the caller
c.caller.ReturnGas(c.Gas, c.Price)
return ret
}
-/*
- * Gas functions
- */
-func (c *Context) UseGas(gas *big.Int) (ok bool) {
- ok = UseGas(c.Gas, gas)
+// UseGas attempts the use gas and subtracts it and returns true on success
+func (c *Contract) UseGas(gas *big.Int) (ok bool) {
+ ok = useGas(c.Gas, gas)
if ok {
c.UsedGas.Add(c.UsedGas, gas)
}
return
}
-// Implement the caller interface
-func (c *Context) ReturnGas(gas, price *big.Int) {
+// ReturnGas adds the given gas back to itself.
+func (c *Contract) ReturnGas(gas, price *big.Int) {
// Return the gas to the context
c.Gas.Add(c.Gas, gas)
c.UsedGas.Sub(c.UsedGas, gas)
}
-/*
- * Set / Get
- */
-func (c *Context) Address() common.Address {
+// Address returns the contracts address
+func (c *Contract) Address() common.Address {
return c.self.Address()
}
-func (self *Context) SetCode(code []byte) {
+// SetCode sets the code to the contract
+func (self *Contract) SetCode(code []byte) {
self.Code = code
}
-func (self *Context) SetCallCode(addr *common.Address, code []byte) {
+// SetCallCode sets the code of the contract and address of the backing data
+// object
+func (self *Contract) SetCallCode(addr *common.Address, code []byte) {
self.Code = code
self.CodeAddr = addr
}
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index b965fa095..22cb9eab2 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -26,22 +26,22 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-type Address interface {
- Call(in []byte) []byte
-}
-
+// PrecompiledAccount represents a native ethereum contract
type PrecompiledAccount struct {
Gas func(l int) *big.Int
fn func(in []byte) []byte
}
+// Call calls the native function
func (self PrecompiledAccount) Call(in []byte) []byte {
return self.fn(in)
}
+// Precompiled contains the default set of ethereum contracts
var Precompiled = PrecompiledContracts()
-// XXX Could set directly. Testing requires resetting and setting of pre compiled contracts.
+// PrecompiledContracts returns the default set of precompiled ethereum
+// contracts defined by the ethereum yellow paper.
func PrecompiledContracts() map[string]*PrecompiledAccount {
return map[string]*PrecompiledAccount{
// ECRECOVER
diff --git a/core/vm/doc.go b/core/vm/doc.go
new file mode 100644
index 000000000..debbdb35e
--- /dev/null
+++ b/core/vm/doc.go
@@ -0,0 +1,35 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// 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.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+/*
+Package vm implements the Ethereum Virtual Machine.
+
+The vm package implements two EVMs, a byte code VM and a JIT VM. The BC
+(Byte Code) VM loops over a set of bytes and executes them according to the set
+of rules defined in the Ethereum yellow paper. When the BC VM is invoked it
+invokes the JIT VM in a seperate goroutine and compiles the byte code in JIT
+instructions.
+
+The JIT VM, when invoked, loops around a set of pre-defined instructions until
+it either runs of gas, causes an internal error, returns or stops.
+
+The JIT optimiser attempts to pre-compile instructions in to chunks or segments
+such as multiple PUSH operations and static JUMPs. It does this by analysing the
+opcodes and attempts to match certain regions to known sets. Whenever the
+optimiser finds said segments it creates a new instruction and replaces the
+first occurrence in the sequence.
+*/
+package vm
diff --git a/core/vm/environment.go b/core/vm/environment.go
index 916081f51..299d12674 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -17,39 +17,86 @@
package vm
import (
- "errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
)
// Environment is is required by the virtual machine to get information from
-// it's own isolated environment. For an example see `core.VMEnv`
-type Environment interface {
- State() *state.StateDB
+// it's own isolated environment.
+// Environment is an EVM requirement and helper which allows access to outside
+// information such as states.
+type Environment interface {
+ // The state database
+ Db() Database
+ // Creates a restorable snapshot
+ MakeSnapshot() Database
+ // Set database to previous snapshot
+ SetSnapshot(Database)
+ // Address of the original invoker (first occurance of the VM invoker)
Origin() common.Address
+ // The block number this VM is invoken on
BlockNumber() *big.Int
- GetHash(n uint64) common.Hash
+ // The n'th hash ago from this block number
+ GetHash(uint64) common.Hash
+ // The handler's address
Coinbase() common.Address
+ // The current time (block time)
Time() *big.Int
+ // Difficulty set on the current block
Difficulty() *big.Int
+ // The gas limit of the block
GasLimit() *big.Int
- CanTransfer(from Account, balance *big.Int) bool
- Transfer(from, to Account, amount *big.Int) error
- AddLog(*state.Log)
+ // Determines whether it's possible to transact
+ CanTransfer(from common.Address, balance *big.Int) bool
+ // Transfers amount from one account to the other
+ Transfer(from, to Account, amount *big.Int)
+ // Adds a LOG to the state
+ AddLog(*Log)
+ // Adds a structured log to the env
AddStructLog(StructLog)
+ // Returns all coalesced structured logs
StructLogs() []StructLog
+ // Type of the VM
VmType() Type
+ // Current calling depth
Depth() int
SetDepth(i int)
- Call(me ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
- CallCode(me ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
- Create(me ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef)
+ // Call another contract
+ Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
+ // Take another's contract code and execute within our own context
+ CallCode(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
+ // Create a new contract
+ Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
+}
+
+// Database is a EVM database for full state querying
+type Database interface {
+ GetAccount(common.Address) Account
+ CreateAccount(common.Address) Account
+
+ AddBalance(common.Address, *big.Int)
+ GetBalance(common.Address) *big.Int
+
+ GetNonce(common.Address) uint64
+ SetNonce(common.Address, uint64)
+
+ GetCode(common.Address) []byte
+ SetCode(common.Address, []byte)
+
+ AddRefund(*big.Int)
+ GetRefund() *big.Int
+
+ GetState(common.Address, common.Hash) common.Hash
+ SetState(common.Address, common.Hash, common.Hash)
+
+ Delete(common.Address) bool
+ Exist(common.Address) bool
+ IsDeleted(common.Address) bool
}
// StructLog is emited to the Environment each cycle and lists information about the curent internal state
@@ -68,18 +115,10 @@ type StructLog struct {
type Account interface {
SubBalance(amount *big.Int)
AddBalance(amount *big.Int)
+ SetBalance(*big.Int)
+ SetNonce(uint64)
Balance() *big.Int
Address() common.Address
-}
-
-// generic transfer method
-func Transfer(from, to Account, amount *big.Int) error {
- if from.Balance().Cmp(amount) < 0 {
- return errors.New("Insufficient balance in account")
- }
-
- from.SubBalance(amount)
- to.AddBalance(amount)
-
- return nil
+ ReturnGas(*big.Int, *big.Int)
+ SetCode([]byte)
}
diff --git a/core/vm/gas.go b/core/vm/gas.go
index b2f068e6e..bff0ac91b 100644
--- a/core/vm/gas.go
+++ b/core/vm/gas.go
@@ -37,6 +37,7 @@ var (
GasContractByte = big.NewInt(200)
)
+// baseCheck checks for any stack error underflows
func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
// PUSH is also allowed to calculate the same price for all PUSHes
@@ -63,6 +64,7 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
return nil
}
+// casts a arbitrary number to the amount of words (sets of 32 bytes)
func toWordSize(size *big.Int) *big.Int {
tmp := new(big.Int)
tmp.Add(size, u256(31))
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index aa0117cc8..2e868521e 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -17,49 +17,123 @@
package vm
import (
+ "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
-type instrFn func(instr instruction, env Environment, context *Context, memory *Memory, stack *stack)
-type instrExFn func(instr instruction, ret *big.Int, env Environment, context *Context, memory *Memory, stack *stack)
+type programInstruction interface {
+ // executes the program instruction and allows the instruction to modify the state of the program
+ do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error)
+ // returns whether the program instruction halts the execution of the JIT
+ halts() bool
+ // Returns the current op code (debugging purposes)
+ Op() OpCode
+}
+
+type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack)
type instruction struct {
- op OpCode
- pc uint64
- fn instrFn
- specFn instrExFn
- data *big.Int
+ op OpCode
+ pc uint64
+ fn instrFn
+ data *big.Int
gas *big.Int
spop int
spush int
+
+ returns bool
+}
+
+func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract *Contract, to *big.Int) (uint64, error) {
+ if !validDest(destinations, to) {
+ nop := contract.GetOp(to.Uint64())
+ return 0, fmt.Errorf("invalid jump destination (%v) %v", nop, to)
+ }
+
+ return mapping[to.Uint64()], nil
+}
+
+func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) {
+ // calculate the new memory size and gas price for the current executing opcode
+ newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack)
+ if err != nil {
+ return nil, err
+ }
+
+ // Use the calculated gas. When insufficient gas is present, use all gas and return an
+ // Out Of Gas error
+ if !contract.UseGas(cost) {
+ return nil, OutOfGasError
+ }
+ // Resize the memory calculated previously
+ memory.Resize(newMemSize.Uint64())
+
+ // These opcodes return an argument and are therefor handled
+ // differently from the rest of the opcodes
+ switch instr.op {
+ case JUMP:
+ if pos, err := jump(program.mapping, program.destinations, contract, stack.pop()); err != nil {
+ return nil, err
+ } else {
+ *pc = pos
+ return nil, nil
+ }
+ case JUMPI:
+ pos, cond := stack.pop(), stack.pop()
+ if cond.Cmp(common.BigTrue) >= 0 {
+ if pos, err := jump(program.mapping, program.destinations, contract, pos); err != nil {
+ return nil, err
+ } else {
+ *pc = pos
+ return nil, nil
+ }
+ }
+ case RETURN:
+ offset, size := stack.pop(), stack.pop()
+ return memory.GetPtr(offset.Int64(), size.Int64()), nil
+ default:
+ if instr.fn == nil {
+ return nil, fmt.Errorf("Invalid opcode 0x%x", instr.op)
+ }
+ instr.fn(instr, pc, env, contract, memory, stack)
+ }
+ *pc++
+ return nil, nil
+}
+
+func (instr instruction) halts() bool {
+ return instr.returns
+}
+
+func (instr instruction) Op() OpCode {
+ return instr.op
}
-func opStaticJump(instr instruction, ret *big.Int, env Environment, context *Context, memory *Memory, stack *stack) {
+func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *stack) {
ret.Set(instr.data)
}
-func opAdd(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Add(x, y)))
}
-func opSub(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Sub(x, y)))
}
-func opMul(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Mul(x, y)))
}
-func opDiv(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) != 0 {
stack.push(U256(x.Div(x, y)))
@@ -68,7 +142,7 @@ func opDiv(instr instruction, env Environment, context *Context, memory *Memory,
}
}
-func opSdiv(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
stack.push(new(big.Int))
@@ -88,7 +162,7 @@ func opSdiv(instr instruction, env Environment, context *Context, memory *Memory
}
}
-func opMod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) == 0 {
stack.push(new(big.Int))
@@ -97,7 +171,7 @@ func opMod(instr instruction, env Environment, context *Context, memory *Memory,
}
}
-func opSmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
@@ -117,12 +191,12 @@ func opSmod(instr instruction, env Environment, context *Context, memory *Memory
}
}
-func opExp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
stack.push(U256(x.Exp(x, y, Pow256)))
}
-func opSignExtend(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
back := stack.pop()
if back.Cmp(big.NewInt(31)) < 0 {
bit := uint(back.Uint64()*8 + 7)
@@ -139,12 +213,12 @@ func opSignExtend(instr instruction, env Environment, context *Context, memory *
}
}
-func opNot(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x := stack.pop()
stack.push(U256(x.Not(x)))
}
-func opLt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) < 0 {
stack.push(big.NewInt(1))
@@ -153,7 +227,7 @@ func opLt(instr instruction, env Environment, context *Context, memory *Memory,
}
}
-func opGt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) > 0 {
stack.push(big.NewInt(1))
@@ -162,7 +236,7 @@ func opGt(instr instruction, env Environment, context *Context, memory *Memory,
}
}
-func opSlt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(S256(y)) < 0 {
stack.push(big.NewInt(1))
@@ -171,7 +245,7 @@ func opSlt(instr instruction, env Environment, context *Context, memory *Memory,
}
}
-func opSgt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(y) > 0 {
stack.push(big.NewInt(1))
@@ -180,7 +254,7 @@ func opSgt(instr instruction, env Environment, context *Context, memory *Memory,
}
}
-func opEq(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
if x.Cmp(y) == 0 {
stack.push(big.NewInt(1))
@@ -189,7 +263,7 @@ func opEq(instr instruction, env Environment, context *Context, memory *Memory,
}
}
-func opIszero(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x := stack.pop()
if x.Cmp(common.Big0) > 0 {
stack.push(new(big.Int))
@@ -198,19 +272,19 @@ func opIszero(instr instruction, env Environment, context *Context, memory *Memo
}
}
-func opAnd(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
stack.push(x.And(x, y))
}
-func opOr(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
stack.push(x.Or(x, y))
}
-func opXor(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop()
stack.push(x.Xor(x, y))
}
-func opByte(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
th, val := stack.pop(), stack.pop()
if th.Cmp(big.NewInt(32)) < 0 {
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
@@ -219,7 +293,7 @@ func opByte(instr instruction, env Environment, context *Context, memory *Memory
stack.push(new(big.Int))
}
}
-func opAddmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(Zero) > 0 {
add := x.Add(x, y)
@@ -229,7 +303,7 @@ func opAddmod(instr instruction, env Environment, context *Context, memory *Memo
stack.push(new(big.Int))
}
}
-func opMulmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(Zero) > 0 {
mul := x.Mul(x, y)
@@ -240,92 +314,92 @@ func opMulmod(instr instruction, env Environment, context *Context, memory *Memo
}
}
-func opSha3(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
offset, size := stack.pop(), stack.pop()
hash := crypto.Sha3(memory.Get(offset.Int64(), size.Int64()))
stack.push(common.BytesToBig(hash))
}
-func opAddress(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- stack.push(common.Bytes2Big(context.Address().Bytes()))
+func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.push(common.Bytes2Big(contract.Address().Bytes()))
}
-func opBalance(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
addr := common.BigToAddress(stack.pop())
- balance := env.State().GetBalance(addr)
+ balance := env.Db().GetBalance(addr)
stack.push(new(big.Int).Set(balance))
}
-func opOrigin(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(env.Origin().Big())
}
-func opCaller(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- stack.push(common.Bytes2Big(context.caller.Address().Bytes()))
+func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.push(common.Bytes2Big(contract.caller.Address().Bytes()))
}
-func opCallValue(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- stack.push(new(big.Int).Set(context.value))
+func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.push(new(big.Int).Set(contract.value))
}
-func opCalldataLoad(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- stack.push(common.Bytes2Big(getData(context.Input, stack.pop(), common.Big32)))
+func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32)))
}
-func opCalldataSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- stack.push(big.NewInt(int64(len(context.Input))))
+func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.push(big.NewInt(int64(len(contract.Input))))
}
-func opCalldataCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
var (
mOff = stack.pop()
cOff = stack.pop()
l = stack.pop()
)
- memory.Set(mOff.Uint64(), l.Uint64(), getData(context.Input, cOff, l))
+ memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
}
-func opExtCodeSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
addr := common.BigToAddress(stack.pop())
- l := big.NewInt(int64(len(env.State().GetCode(addr))))
+ l := big.NewInt(int64(len(env.Db().GetCode(addr))))
stack.push(l)
}
-func opCodeSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- l := big.NewInt(int64(len(context.Code)))
+func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ l := big.NewInt(int64(len(contract.Code)))
stack.push(l)
}
-func opCodeCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
var (
mOff = stack.pop()
cOff = stack.pop()
l = stack.pop()
)
- codeCopy := getData(context.Code, cOff, l)
+ codeCopy := getData(contract.Code, cOff, l)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
}
-func opExtCodeCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
var (
addr = common.BigToAddress(stack.pop())
mOff = stack.pop()
cOff = stack.pop()
l = stack.pop()
)
- codeCopy := getData(env.State().GetCode(addr), cOff, l)
+ codeCopy := getData(env.Db().GetCode(addr), cOff, l)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
}
-func opGasprice(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- stack.push(new(big.Int).Set(context.Price))
+func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.push(new(big.Int).Set(contract.Price))
}
-func opBlockhash(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
num := stack.pop()
n := new(big.Int).Sub(env.BlockNumber(), common.Big257)
@@ -336,43 +410,43 @@ func opBlockhash(instr instruction, env Environment, context *Context, memory *M
}
}
-func opCoinbase(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(env.Coinbase().Big())
}
-func opTimestamp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(U256(new(big.Int).Set(env.Time())))
}
-func opNumber(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(U256(new(big.Int).Set(env.BlockNumber())))
}
-func opDifficulty(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(U256(new(big.Int).Set(env.Difficulty())))
}
-func opGasLimit(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(U256(new(big.Int).Set(env.GasLimit())))
}
-func opPop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.pop()
}
-func opPush(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(new(big.Int).Set(instr.data))
}
-func opDup(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.dup(int(instr.data.Int64()))
}
-func opSwap(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.swap(int(instr.data.Int64()))
}
-func opLog(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
n := int(instr.data.Int64())
topics := make([]common.Hash, n)
mStart, mSize := stack.pop(), stack.pop()
@@ -381,85 +455,88 @@ func opLog(instr instruction, env Environment, context *Context, memory *Memory,
}
d := memory.Get(mStart.Int64(), mSize.Int64())
- log := state.NewLog(context.Address(), topics, d, env.BlockNumber().Uint64())
+ log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
env.AddLog(log)
}
-func opMload(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
offset := stack.pop()
val := common.BigD(memory.Get(offset.Int64(), 32))
stack.push(val)
}
-func opMstore(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
// pop value of the stack
mStart, val := stack.pop(), stack.pop()
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
}
-func opMstore8(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
off, val := stack.pop().Int64(), stack.pop().Int64()
memory.store[off] = byte(val & 0xff)
}
-func opSload(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
loc := common.BigToHash(stack.pop())
- val := env.State().GetState(context.Address(), loc).Big()
+ val := env.Db().GetState(contract.Address(), loc).Big()
stack.push(val)
}
-func opSstore(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
loc := common.BigToHash(stack.pop())
val := stack.pop()
- env.State().SetState(context.Address(), loc, common.BigToHash(val))
+ env.Db().SetState(contract.Address(), loc, common.BigToHash(val))
}
-func opJump(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
-func opJumpi(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
-func opJumpdest(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
+func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+}
+func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+}
+func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+}
-func opPc(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(new(big.Int).Set(instr.data))
}
-func opMsize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(big.NewInt(int64(memory.Len())))
}
-func opGas(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- stack.push(new(big.Int).Set(context.Gas))
+func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.push(new(big.Int).Set(contract.Gas))
}
-func opCreate(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
var (
value = stack.pop()
offset, size = stack.pop(), stack.pop()
input = memory.Get(offset.Int64(), size.Int64())
- gas = new(big.Int).Set(context.Gas)
+ gas = new(big.Int).Set(contract.Gas)
addr common.Address
+ ret []byte
+ suberr error
)
- context.UseGas(context.Gas)
- ret, suberr, ref := env.Create(context, input, gas, context.Price, value)
+ contract.UseGas(contract.Gas)
+ ret, addr, suberr = 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 context.UseGas(dataGas) {
- ref.SetCode(ret)
+ if contract.UseGas(dataGas) {
+ env.Db().SetCode(addr, ret)
}
- addr = ref.Address()
stack.push(addr.Big())
}
}
-func opCall(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
@@ -478,7 +555,7 @@ func opCall(instr instruction, env Environment, context *Context, memory *Memory
gas.Add(gas, params.CallStipend)
}
- ret, err := env.Call(context, address, args, gas, context.Price, value)
+ ret, err := env.Call(contract, address, args, gas, contract.Price, value)
if err != nil {
stack.push(new(big.Int))
@@ -490,7 +567,7 @@ func opCall(instr instruction, env Environment, context *Context, memory *Memory
}
}
-func opCallCode(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
+func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
@@ -509,7 +586,7 @@ func opCallCode(instr instruction, env Environment, context *Context, memory *Me
gas.Add(gas, params.CallStipend)
}
- ret, err := env.CallCode(context, address, args, gas, context.Price, value)
+ ret, err := env.CallCode(contract, address, args, gas, contract.Price, value)
if err != nil {
stack.push(new(big.Int))
@@ -521,14 +598,56 @@ func opCallCode(instr instruction, env Environment, context *Context, memory *Me
}
}
-func opReturn(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
-func opStop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {}
+func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+}
+func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+}
+
+func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ balance := env.Db().GetBalance(contract.Address())
+ env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
-func opSuicide(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {
- receiver := env.State().GetOrNewStateObject(common.BigToAddress(stack.pop()))
- balance := env.State().GetBalance(context.Address())
+ env.Db().Delete(contract.Address())
+}
- receiver.AddBalance(balance)
+// following functions are used by the instruction jump table
+
+// make log instruction function
+func makeLog(size int) instrFn {
+ return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ topics := make([]common.Hash, size)
+ mStart, mSize := stack.pop(), stack.pop()
+ for i := 0; i < size; i++ {
+ topics[i] = common.BigToHash(stack.pop())
+ }
- env.State().Delete(context.Address())
+ d := memory.Get(mStart.Int64(), mSize.Int64())
+ log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
+ env.AddLog(log)
+ }
+}
+
+// make push instruction function
+func makePush(size uint64, bsize *big.Int) instrFn {
+ return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
+ stack.push(common.Bytes2Big(byts))
+ *pc += size
+ }
+}
+
+// make push instruction function
+func makeDup(size int64) instrFn {
+ return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.dup(int(size))
+ }
+}
+
+// make swap instruction function
+func makeSwap(size int64) instrFn {
+ // switch n + 1 otherwise n would be swapped with n
+ size += 1
+ return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ stack.swap(int(size))
+ }
}
diff --git a/core/vm/jit.go b/core/vm/jit.go
index 084d2a3f3..1aa7d7ef2 100644
--- a/core/vm/jit.go
+++ b/core/vm/jit.go
@@ -20,10 +20,12 @@ import (
"fmt"
"math/big"
"sync/atomic"
+ "time"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"github.com/hashicorp/golang-lru"
)
@@ -35,6 +37,14 @@ const (
progCompile
progReady
progError
+
+ defaultJitMaxCache int = 64
+)
+
+var (
+ EnableJit bool // Enables the JIT VM
+ ForceJit bool // Force the JIT, skip byte VM
+ MaxProgSize int // Max cache size for JIT Programs
)
var programs *lru.Cache
@@ -74,11 +84,11 @@ type Program struct {
Id common.Hash // Id of the program
status int32 // status should be accessed atomically
- context *Context
+ contract *Contract
- instructions []instruction // instruction set
- mapping map[uint64]int // real PC mapping to array indices
- destinations map[uint64]struct{} // cached jump destinations
+ instructions []programInstruction // instruction set
+ mapping map[uint64]uint64 // real PC mapping to array indices
+ destinations map[uint64]struct{} // cached jump destinations
code []byte
}
@@ -87,7 +97,7 @@ type Program struct {
func NewProgram(code []byte) *Program {
program := &Program{
Id: crypto.Sha3Hash(code),
- mapping: make(map[uint64]int),
+ mapping: make(map[uint64]uint64),
destinations: make(map[uint64]struct{}),
code: code,
}
@@ -108,10 +118,12 @@ func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) {
baseOp = DUP1
}
base := _baseCheck[baseOp]
- instr := instruction{op, pc, fn, nil, data, base.gas, base.stackPop, base.stackPush}
+
+ returns := op == RETURN || op == SUICIDE || op == STOP
+ instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush, returns}
p.instructions = append(p.instructions, instr)
- p.mapping[pc] = len(p.instructions) - 1
+ p.mapping[pc] = uint64(len(p.instructions) - 1)
}
// CompileProgram compiles the given program and return an error when it fails
@@ -127,6 +139,13 @@ func CompileProgram(program *Program) (err error) {
atomic.StoreInt32(&program.status, int32(progReady))
}
}()
+ if glog.V(logger.Debug) {
+ glog.Infof("compiling %x\n", program.Id[:4])
+ tstart := time.Now()
+ defer func() {
+ glog.Infof("compiled %x instrc: %d time: %v\n", program.Id[:4], len(program.instructions), time.Since(tstart))
+ }()
+ }
// loop thru the opcodes and "compile" in to instructions
for pc := uint64(0); pc < uint64(len(program.code)); pc++ {
@@ -264,101 +283,58 @@ func CompileProgram(program *Program) (err error) {
program.addInstr(op, pc, opReturn, nil)
case SUICIDE:
program.addInstr(op, pc, opSuicide, nil)
- case STOP: // Stop the context
+ case STOP: // Stop the contract
program.addInstr(op, pc, opStop, nil)
default:
program.addInstr(op, pc, nil, nil)
}
}
+ optimiseProgram(program)
+
return nil
}
-// RunProgram runs the program given the enviroment and context and returns an
+// RunProgram runs the program given the enviroment and contract and returns an
// error if the execution failed (non-consensus)
-func RunProgram(program *Program, env Environment, context *Context, input []byte) ([]byte, error) {
- return runProgram(program, 0, NewMemory(), newstack(), env, context, input)
+func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) {
+ return runProgram(program, 0, NewMemory(), newstack(), env, contract, input)
}
-func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, context *Context, input []byte) ([]byte, error) {
- context.Input = input
+func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, contract *Contract, input []byte) ([]byte, error) {
+ contract.Input = input
var (
- caller = context.caller
- statedb = env.State()
- pc int = program.mapping[pcstart]
-
- jump = func(to *big.Int) error {
- if !validDest(program.destinations, to) {
- nop := context.GetOp(to.Uint64())
- return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
- }
+ pc uint64 = program.mapping[pcstart]
+ instrCount = 0
+ )
- pc = program.mapping[to.Uint64()]
+ if glog.V(logger.Debug) {
+ glog.Infof("running JIT program %x\n", program.Id[:4])
+ tstart := time.Now()
+ defer func() {
+ glog.Infof("JIT program %x done. time: %v instrc: %v\n", program.Id[:4], time.Since(tstart), instrCount)
+ }()
+ }
- return nil
- }
- )
+ for pc < uint64(len(program.instructions)) {
+ instrCount++
- for pc < len(program.instructions) {
instr := program.instructions[pc]
- // calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err := jitCalculateGasAndSize(env, context, caller, instr, statedb, mem, stack)
+ ret, err := instr.do(program, &pc, env, contract, mem, stack)
if err != nil {
return nil, err
}
- // Use the calculated gas. When insufficient gas is present, use all gas and return an
- // Out Of Gas error
- if !context.UseGas(cost) {
- return nil, OutOfGasError
+ if instr.halts() {
+ return contract.Return(ret), nil
}
- // Resize the memory calculated previously
- mem.Resize(newMemSize.Uint64())
-
- // These opcodes return an argument and are thefor handled
- // differently from the rest of the opcodes
- switch instr.op {
- case JUMP:
- if err := jump(stack.pop()); err != nil {
- return nil, err
- }
- continue
- case JUMPI:
- pos, cond := stack.pop(), stack.pop()
-
- if cond.Cmp(common.BigTrue) >= 0 {
- if err := jump(pos); err != nil {
- return nil, err
- }
- continue
- }
- case RETURN:
- offset, size := stack.pop(), stack.pop()
- ret := mem.GetPtr(offset.Int64(), size.Int64())
-
- return context.Return(ret), nil
- case SUICIDE:
- instr.fn(instr, env, context, mem, stack)
-
- return context.Return(nil), nil
- case STOP:
- return context.Return(nil), nil
- default:
- if instr.fn == nil {
- return nil, fmt.Errorf("Invalid opcode %x", instr.op)
- }
-
- instr.fn(instr, env, context, mem, stack)
- }
-
- pc++
}
- context.Input = nil
+ contract.Input = nil
- return context.Return(nil), nil
+ return contract.Return(nil), nil
}
// validDest checks if the given distination is a valid one given the
@@ -375,7 +351,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
// jitCalculateGasAndSize 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.
-func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef, instr instruction, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
+func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -426,27 +402,25 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef
var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
- val := statedb.GetState(context.Address(), common.BigToHash(x))
+ val := statedb.GetState(contract.Address(), common.BigToHash(x))
// 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)
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
- // 0 => non 0
g = params.SstoreSetGas
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- statedb.Refund(params.SstoreRefundGas)
+ statedb.AddRefund(params.SstoreRefundGas)
g = params.SstoreClearGas
} else {
- // non 0 => non 0 (or 0 => 0)
g = params.SstoreClearGas
}
gas.Set(g)
case SUICIDE:
- if !statedb.IsDeleted(context.Address()) {
- statedb.Refund(params.SuicideRefundGas)
+ if !statedb.IsDeleted(contract.Address()) {
+ statedb.AddRefund(params.SuicideRefundGas)
}
case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32))
@@ -483,7 +457,8 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef
gas.Add(gas, stack.data[stack.len()-1])
if op == CALL {
- if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
+ //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
+ if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
gas.Add(gas, params.CallNewAccountGas)
}
}
@@ -497,29 +472,7 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef
newMemSize = common.BigMax(x, y)
}
-
- if newMemSize.Cmp(common.Big0) > 0 {
- newMemSizeWords := toWordSize(newMemSize)
- newMemSize.Mul(newMemSizeWords, u256(32))
-
- if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
- // be careful reusing variables here when changing.
- // The order has been optimised to reduce allocation
- oldSize := toWordSize(big.NewInt(int64(mem.Len())))
- pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
- linCoef := oldSize.Mul(oldSize, params.MemoryGas)
- quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
- oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
-
- pow.Exp(newMemSizeWords, common.Big2, Zero)
- linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
- quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
- newTotalFee := linCoef.Add(linCoef, quadCoef)
-
- fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
- gas.Add(gas, fee)
- }
- }
+ quadMemGas(mem, newMemSize, gas)
return newMemSize, gas, nil
}
diff --git a/core/vm/jit_optimiser.go b/core/vm/jit_optimiser.go
new file mode 100644
index 000000000..845ffbbdf
--- /dev/null
+++ b/core/vm/jit_optimiser.go
@@ -0,0 +1,107 @@
+package vm
+
+import (
+ "math/big"
+ "time"
+
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+)
+
+// optimeProgram optimises a JIT program creating segments out of program
+// instructions. Currently covered are multi-pushes and static jumps
+func optimiseProgram(program *Program) {
+ var load []instruction
+
+ var (
+ statsJump = 0
+ statsPush = 0
+ )
+
+ if glog.V(logger.Debug) {
+ glog.Infof("optimising %x\n", program.Id[:4])
+ tstart := time.Now()
+ defer func() {
+ glog.Infof("optimised %x done in %v with JMP: %d PSH: %d\n", program.Id[:4], time.Since(tstart), statsJump, statsPush)
+ }()
+ }
+
+ /*
+ code := Parse(program.code)
+ for _, test := range [][]OpCode{
+ []OpCode{PUSH, PUSH, ADD},
+ []OpCode{PUSH, PUSH, SUB},
+ []OpCode{PUSH, PUSH, MUL},
+ []OpCode{PUSH, PUSH, DIV},
+ } {
+ matchCount := 0
+ MatchFn(code, test, func(i int) bool {
+ matchCount++
+ return true
+ })
+ fmt.Printf("found %d match count on: %v\n", matchCount, test)
+ }
+ */
+
+ for i := 0; i < len(program.instructions); i++ {
+ instr := program.instructions[i].(instruction)
+
+ switch {
+ case instr.op.IsPush():
+ load = append(load, instr)
+ case instr.op.IsStaticJump():
+ if len(load) == 0 {
+ continue
+ }
+ // if the push load is greater than 1, finalise that
+ // segment first
+ if len(load) > 2 {
+ seg, size := makePushSeg(load[:len(load)-1])
+ program.instructions[i-size-1] = seg
+ statsPush++
+ }
+ // create a segment consisting of a pre determined
+ // jump, destination and validity.
+ seg := makeStaticJumpSeg(load[len(load)-1].data, program)
+ program.instructions[i-1] = seg
+ statsJump++
+
+ load = nil
+ default:
+ // create a new N pushes segment
+ if len(load) > 1 {
+ seg, size := makePushSeg(load)
+ program.instructions[i-size] = seg
+ statsPush++
+ }
+ load = nil
+ }
+ }
+}
+
+// makePushSeg creates a new push segment from N amount of push instructions
+func makePushSeg(instrs []instruction) (pushSeg, int) {
+ var (
+ data []*big.Int
+ gas = new(big.Int)
+ )
+
+ for _, instr := range instrs {
+ data = append(data, instr.data)
+ gas.Add(gas, instr.gas)
+ }
+
+ return pushSeg{data, gas}, len(instrs)
+}
+
+// makeStaticJumpSeg creates a new static jump segment from a predefined
+// destination (PUSH, JUMP).
+func makeStaticJumpSeg(to *big.Int, program *Program) jumpSeg {
+ gas := new(big.Int)
+ gas.Add(gas, _baseCheck[PUSH1].gas)
+ gas.Add(gas, _baseCheck[JUMP].gas)
+
+ contract := &Contract{Code: program.code}
+ pos, err := jump(program.mapping, program.destinations, contract, to)
+ return jumpSeg{pos, err, gas}
+}
diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go
index d8e442637..aa97e5184 100644
--- a/core/vm/jit_test.go
+++ b/core/vm/jit_test.go
@@ -21,13 +21,99 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
)
const maxRun = 1000
+func TestSegmenting(t *testing.T) {
+ prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0})
+ err := CompileProgram(prog)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if instr, ok := prog.instructions[0].(pushSeg); ok {
+ if len(instr.data) != 2 {
+ t.Error("expected 2 element width pushSegment, got", len(instr.data))
+ }
+ } else {
+ t.Errorf("expected instr[0] to be a pushSeg, got %T", prog.instructions[0])
+ }
+
+ prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
+ err = CompileProgram(prog)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, ok := prog.instructions[1].(jumpSeg); ok {
+ } else {
+ t.Errorf("expected instr[1] to be jumpSeg, got %T", prog.instructions[1])
+ }
+
+ prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)})
+ err = CompileProgram(prog)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if instr, ok := prog.instructions[0].(pushSeg); ok {
+ if len(instr.data) != 2 {
+ t.Error("expected 2 element width pushSegment, got", len(instr.data))
+ }
+ } else {
+ t.Errorf("expected instr[0] to be a pushSeg, got %T", prog.instructions[0])
+ }
+ if _, ok := prog.instructions[2].(jumpSeg); ok {
+ } else {
+ t.Errorf("expected instr[1] to be jumpSeg, got %T", prog.instructions[1])
+ }
+}
+
+func TestCompiling(t *testing.T) {
+ prog := NewProgram([]byte{0x60, 0x10})
+ err := CompileProgram(prog)
+ if err != nil {
+ t.Error("didn't expect compile error")
+ }
+
+ if len(prog.instructions) != 1 {
+ t.Error("exected 1 compiled instruction, got", len(prog.instructions))
+ }
+}
+
+func TestResetInput(t *testing.T) {
+ var sender account
+
+ env := NewEnv()
+ contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
+ contract.CodeAddr = &common.Address{}
+
+ program := NewProgram([]byte{})
+ RunProgram(program, env, contract, []byte{0xbe, 0xef})
+ if contract.Input != nil {
+ t.Errorf("expected input to be nil, got %x", contract.Input)
+ }
+}
+
+func TestPcMappingToInstruction(t *testing.T) {
+ program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)})
+ CompileProgram(program)
+ if program.mapping[3] != 1 {
+ t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4])
+ }
+}
+
+var benchmarks = map[string]vmBench{
+ "pushes": vmBench{
+ false, false, false,
+ common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil,
+ },
+}
+
+func BenchmarkPushes(b *testing.B) {
+ runVmBench(benchmarks["pushes"], b)
+}
+
type vmBench struct {
precompile bool // compile prior to executing
nojit bool // ignore jit (sets DisbaleJit = true
@@ -37,9 +123,19 @@ type vmBench struct {
input []byte
}
+type account struct{}
+
+func (account) SubBalance(amount *big.Int) {}
+func (account) AddBalance(amount *big.Int) {}
+func (account) SetBalance(*big.Int) {}
+func (account) SetNonce(uint64) {}
+func (account) Balance() *big.Int { return nil }
+func (account) Address() common.Address { return common.Address{} }
+func (account) ReturnGas(*big.Int, *big.Int) {}
+func (account) SetCode([]byte) {}
+
func runVmBench(test vmBench, b *testing.B) {
- db, _ := ethdb.NewMemDatabase()
- sender := state.NewStateObject(common.Address{}, db)
+ var sender account
if test.precompile && !test.forcejit {
NewProgram(test.code)
@@ -52,7 +148,7 @@ func runVmBench(test vmBench, b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
- context := NewContext(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
+ context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
context.Code = test.code
context.CodeAddr = &common.Address{}
_, err := New(env).Run(context, test.input)
@@ -63,17 +159,6 @@ func runVmBench(test vmBench, b *testing.B) {
}
}
-var benchmarks = map[string]vmBench{
- "pushes": vmBench{
- false, false, false,
- common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil,
- },
-}
-
-func BenchmarkPushes(b *testing.B) {
- runVmBench(benchmarks["pushes"], b)
-}
-
type Env struct {
gasLimit *big.Int
depth int
@@ -93,30 +178,30 @@ func (self *Env) StructLogs() []StructLog {
//func (self *Env) PrevHash() []byte { return self.parent }
func (self *Env) Coinbase() common.Address { return common.Address{} }
+func (self *Env) MakeSnapshot() Database { return nil }
+func (self *Env) SetSnapshot(Database) {}
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
-func (self *Env) State() *state.StateDB { return nil }
+func (self *Env) Db() Database { return nil }
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) VmType() Type { return StdVmTy }
func (self *Env) GetHash(n uint64) common.Hash {
return common.BytesToHash(crypto.Sha3([]byte(big.NewInt(int64(n)).String())))
}
-func (self *Env) AddLog(log *state.Log) {
+func (self *Env) AddLog(log *Log) {
}
func (self *Env) Depth() int { return self.depth }
func (self *Env) SetDepth(i int) { self.depth = i }
-func (self *Env) CanTransfer(from Account, balance *big.Int) bool {
- return from.Balance().Cmp(balance) >= 0
-}
-func (self *Env) Transfer(from, to Account, amount *big.Int) error {
- return nil
+func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
+ return true
}
-func (self *Env) Call(caller ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
+func (self *Env) Transfer(from, to Account, amount *big.Int) {}
+func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return nil, nil
}
-func (self *Env) CallCode(caller ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
+func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return nil, nil
}
-func (self *Env) Create(caller ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef) {
- return nil, nil, nil
+func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
+ return nil, common.Address{}, nil
}
diff --git a/core/vm/jit_util.go b/core/vm/jit_util.go
new file mode 100644
index 000000000..0d3d6d701
--- /dev/null
+++ b/core/vm/jit_util.go
@@ -0,0 +1,68 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// 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.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package vm
+
+// Parse parses all opcodes from the given code byte slice. This function
+// performs no error checking and may return non-existing opcodes.
+func Parse(code []byte) (opcodes []OpCode) {
+ for pc := uint64(0); pc < uint64(len(code)); pc++ {
+ op := OpCode(code[pc])
+
+ switch op {
+ case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
+ a := uint64(op) - uint64(PUSH1) + 1
+ pc += a
+ opcodes = append(opcodes, PUSH)
+ case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
+ opcodes = append(opcodes, DUP)
+ case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
+ opcodes = append(opcodes, SWAP)
+ default:
+ opcodes = append(opcodes, op)
+ }
+ }
+
+ return opcodes
+}
+
+// MatchFn searcher for match in the given input and calls matcheFn if it finds
+// an appropriate match. matcherFn yields the starting position in the input.
+// MatchFn will continue to search for a match until it reacher the end of the
+// buffer or if matcherFn return false.
+func MatchFn(input, match []OpCode, matcherFn func(int) bool) {
+ // short circuit if either input or match is empty or if the match is
+ // greater than the input
+ if len(input) == 0 || len(match) == 0 || len(match) > len(input) {
+ return
+ }
+
+main:
+ for i, op := range input[:len(input)+1-len(match)] {
+ // match first opcode and continue search
+ if op == match[0] {
+ for j := 1; j < len(match); j++ {
+ if input[i+j] != match[j] {
+ continue main
+ }
+ }
+ // check for abort instruction
+ if !matcherFn(i) {
+ return
+ }
+ }
+ }
+}
diff --git a/core/vm/jit_util_test.go b/core/vm/jit_util_test.go
new file mode 100644
index 000000000..686bee0ac
--- /dev/null
+++ b/core/vm/jit_util_test.go
@@ -0,0 +1,84 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// 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.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package vm
+
+import "testing"
+
+type matchTest struct {
+ input []OpCode
+ match []OpCode
+ matches int
+}
+
+func TestMatchFn(t *testing.T) {
+ tests := []matchTest{
+ matchTest{
+ []OpCode{PUSH1, PUSH1, MSTORE, JUMP},
+ []OpCode{PUSH1, MSTORE},
+ 1,
+ },
+ matchTest{
+ []OpCode{PUSH1, PUSH1, MSTORE, JUMP},
+ []OpCode{PUSH1, MSTORE, PUSH1},
+ 0,
+ },
+ matchTest{
+ []OpCode{},
+ []OpCode{PUSH1},
+ 0,
+ },
+ }
+
+ for i, test := range tests {
+ var matchCount int
+ MatchFn(test.input, test.match, func(i int) bool {
+ matchCount++
+ return true
+ })
+ if matchCount != test.matches {
+ t.Errorf("match count failed on test[%d]: expected %d matches, got %d", i, test.matches, matchCount)
+ }
+ }
+}
+
+type parseTest struct {
+ base OpCode
+ size int
+ output OpCode
+}
+
+func TestParser(t *testing.T) {
+ tests := []parseTest{
+ parseTest{PUSH1, 32, PUSH},
+ parseTest{DUP1, 16, DUP},
+ parseTest{SWAP1, 16, SWAP},
+ parseTest{MSTORE, 1, MSTORE},
+ }
+
+ for _, test := range tests {
+ for i := 0; i < test.size; i++ {
+ code := append([]byte{byte(byte(test.base) + byte(i))}, make([]byte, i+1)...)
+ output := Parse(code)
+ if len(output) == 0 {
+ t.Fatal("empty output")
+ }
+ if output[0] != test.output {
+ t.Error("%v failed: expected %v but got %v", test.base+OpCode(i), output[0])
+ }
+ }
+ }
+}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
new file mode 100644
index 000000000..ab899647f
--- /dev/null
+++ b/core/vm/jump_table.go
@@ -0,0 +1,143 @@
+package vm
+
+import "math/big"
+
+type jumpPtr struct {
+ fn instrFn
+ valid bool
+}
+
+var jumpTable [256]jumpPtr
+
+func init() {
+ jumpTable[ADD] = jumpPtr{opAdd, true}
+ jumpTable[SUB] = jumpPtr{opSub, true}
+ jumpTable[MUL] = jumpPtr{opMul, true}
+ jumpTable[DIV] = jumpPtr{opDiv, true}
+ jumpTable[SDIV] = jumpPtr{opSdiv, true}
+ jumpTable[MOD] = jumpPtr{opMod, true}
+ jumpTable[SMOD] = jumpPtr{opSmod, true}
+ jumpTable[EXP] = jumpPtr{opExp, true}
+ jumpTable[SIGNEXTEND] = jumpPtr{opSignExtend, true}
+ jumpTable[NOT] = jumpPtr{opNot, true}
+ jumpTable[LT] = jumpPtr{opLt, true}
+ jumpTable[GT] = jumpPtr{opGt, true}
+ jumpTable[SLT] = jumpPtr{opSlt, true}
+ jumpTable[SGT] = jumpPtr{opSgt, true}
+ jumpTable[EQ] = jumpPtr{opEq, true}
+ jumpTable[ISZERO] = jumpPtr{opIszero, true}
+ jumpTable[AND] = jumpPtr{opAnd, true}
+ jumpTable[OR] = jumpPtr{opOr, true}
+ jumpTable[XOR] = jumpPtr{opXor, true}
+ jumpTable[BYTE] = jumpPtr{opByte, true}
+ jumpTable[ADDMOD] = jumpPtr{opAddmod, true}
+ jumpTable[MULMOD] = jumpPtr{opMulmod, true}
+ jumpTable[SHA3] = jumpPtr{opSha3, true}
+ jumpTable[ADDRESS] = jumpPtr{opAddress, true}
+ jumpTable[BALANCE] = jumpPtr{opBalance, true}
+ jumpTable[ORIGIN] = jumpPtr{opOrigin, true}
+ jumpTable[CALLER] = jumpPtr{opCaller, true}
+ jumpTable[CALLVALUE] = jumpPtr{opCallValue, true}
+ jumpTable[CALLDATALOAD] = jumpPtr{opCalldataLoad, true}
+ jumpTable[CALLDATASIZE] = jumpPtr{opCalldataSize, true}
+ jumpTable[CALLDATACOPY] = jumpPtr{opCalldataCopy, true}
+ jumpTable[CODESIZE] = jumpPtr{opCodeSize, true}
+ jumpTable[EXTCODESIZE] = jumpPtr{opExtCodeSize, true}
+ jumpTable[CODECOPY] = jumpPtr{opCodeCopy, true}
+ jumpTable[EXTCODECOPY] = jumpPtr{opExtCodeCopy, true}
+ jumpTable[GASPRICE] = jumpPtr{opGasprice, true}
+ jumpTable[BLOCKHASH] = jumpPtr{opBlockhash, true}
+ jumpTable[COINBASE] = jumpPtr{opCoinbase, true}
+ jumpTable[TIMESTAMP] = jumpPtr{opTimestamp, true}
+ jumpTable[NUMBER] = jumpPtr{opNumber, true}
+ jumpTable[DIFFICULTY] = jumpPtr{opDifficulty, true}
+ jumpTable[GASLIMIT] = jumpPtr{opGasLimit, true}
+ jumpTable[POP] = jumpPtr{opPop, true}
+ jumpTable[MLOAD] = jumpPtr{opMload, true}
+ jumpTable[MSTORE] = jumpPtr{opMstore, true}
+ jumpTable[MSTORE8] = jumpPtr{opMstore8, true}
+ jumpTable[SLOAD] = jumpPtr{opSload, true}
+ jumpTable[SSTORE] = jumpPtr{opSstore, true}
+ jumpTable[JUMPDEST] = jumpPtr{opJumpdest, true}
+ jumpTable[PC] = jumpPtr{nil, true}
+ jumpTable[MSIZE] = jumpPtr{opMsize, true}
+ jumpTable[GAS] = jumpPtr{opGas, true}
+ jumpTable[CREATE] = jumpPtr{opCreate, true}
+ jumpTable[CALL] = jumpPtr{opCall, true}
+ jumpTable[CALLCODE] = jumpPtr{opCallCode, true}
+ jumpTable[LOG0] = jumpPtr{makeLog(0), true}
+ jumpTable[LOG1] = jumpPtr{makeLog(1), true}
+ jumpTable[LOG2] = jumpPtr{makeLog(2), true}
+ jumpTable[LOG3] = jumpPtr{makeLog(3), true}
+ jumpTable[LOG4] = jumpPtr{makeLog(4), true}
+ jumpTable[SWAP1] = jumpPtr{makeSwap(1), true}
+ jumpTable[SWAP2] = jumpPtr{makeSwap(2), true}
+ jumpTable[SWAP3] = jumpPtr{makeSwap(3), true}
+ jumpTable[SWAP4] = jumpPtr{makeSwap(4), true}
+ jumpTable[SWAP5] = jumpPtr{makeSwap(5), true}
+ jumpTable[SWAP6] = jumpPtr{makeSwap(6), true}
+ jumpTable[SWAP7] = jumpPtr{makeSwap(7), true}
+ jumpTable[SWAP8] = jumpPtr{makeSwap(8), true}
+ jumpTable[SWAP9] = jumpPtr{makeSwap(9), true}
+ jumpTable[SWAP10] = jumpPtr{makeSwap(10), true}
+ jumpTable[SWAP11] = jumpPtr{makeSwap(11), true}
+ jumpTable[SWAP12] = jumpPtr{makeSwap(12), true}
+ jumpTable[SWAP13] = jumpPtr{makeSwap(13), true}
+ jumpTable[SWAP14] = jumpPtr{makeSwap(14), true}
+ jumpTable[SWAP15] = jumpPtr{makeSwap(15), true}
+ jumpTable[SWAP16] = jumpPtr{makeSwap(16), true}
+ jumpTable[PUSH1] = jumpPtr{makePush(1, big.NewInt(1)), true}
+ jumpTable[PUSH2] = jumpPtr{makePush(2, big.NewInt(2)), true}
+ jumpTable[PUSH3] = jumpPtr{makePush(3, big.NewInt(3)), true}
+ jumpTable[PUSH4] = jumpPtr{makePush(4, big.NewInt(4)), true}
+ jumpTable[PUSH5] = jumpPtr{makePush(5, big.NewInt(5)), true}
+ jumpTable[PUSH6] = jumpPtr{makePush(6, big.NewInt(6)), true}
+ jumpTable[PUSH7] = jumpPtr{makePush(7, big.NewInt(7)), true}
+ jumpTable[PUSH8] = jumpPtr{makePush(8, big.NewInt(8)), true}
+ jumpTable[PUSH9] = jumpPtr{makePush(9, big.NewInt(9)), true}
+ jumpTable[PUSH10] = jumpPtr{makePush(10, big.NewInt(10)), true}
+ jumpTable[PUSH11] = jumpPtr{makePush(11, big.NewInt(11)), true}
+ jumpTable[PUSH12] = jumpPtr{makePush(12, big.NewInt(12)), true}
+ jumpTable[PUSH13] = jumpPtr{makePush(13, big.NewInt(13)), true}
+ jumpTable[PUSH14] = jumpPtr{makePush(14, big.NewInt(14)), true}
+ jumpTable[PUSH15] = jumpPtr{makePush(15, big.NewInt(15)), true}
+ jumpTable[PUSH16] = jumpPtr{makePush(16, big.NewInt(16)), true}
+ jumpTable[PUSH17] = jumpPtr{makePush(17, big.NewInt(17)), true}
+ jumpTable[PUSH18] = jumpPtr{makePush(18, big.NewInt(18)), true}
+ jumpTable[PUSH19] = jumpPtr{makePush(19, big.NewInt(19)), true}
+ jumpTable[PUSH20] = jumpPtr{makePush(20, big.NewInt(20)), true}
+ jumpTable[PUSH21] = jumpPtr{makePush(21, big.NewInt(21)), true}
+ jumpTable[PUSH22] = jumpPtr{makePush(22, big.NewInt(22)), true}
+ jumpTable[PUSH23] = jumpPtr{makePush(23, big.NewInt(23)), true}
+ jumpTable[PUSH24] = jumpPtr{makePush(24, big.NewInt(24)), true}
+ jumpTable[PUSH25] = jumpPtr{makePush(25, big.NewInt(25)), true}
+ jumpTable[PUSH26] = jumpPtr{makePush(26, big.NewInt(26)), true}
+ jumpTable[PUSH27] = jumpPtr{makePush(27, big.NewInt(27)), true}
+ jumpTable[PUSH28] = jumpPtr{makePush(28, big.NewInt(28)), true}
+ jumpTable[PUSH29] = jumpPtr{makePush(29, big.NewInt(29)), true}
+ jumpTable[PUSH30] = jumpPtr{makePush(30, big.NewInt(30)), true}
+ jumpTable[PUSH31] = jumpPtr{makePush(31, big.NewInt(31)), true}
+ jumpTable[PUSH32] = jumpPtr{makePush(32, big.NewInt(32)), true}
+ jumpTable[DUP1] = jumpPtr{makeDup(1), true}
+ jumpTable[DUP2] = jumpPtr{makeDup(2), true}
+ jumpTable[DUP3] = jumpPtr{makeDup(3), true}
+ jumpTable[DUP4] = jumpPtr{makeDup(4), true}
+ jumpTable[DUP5] = jumpPtr{makeDup(5), true}
+ jumpTable[DUP6] = jumpPtr{makeDup(6), true}
+ jumpTable[DUP7] = jumpPtr{makeDup(7), true}
+ jumpTable[DUP8] = jumpPtr{makeDup(8), true}
+ jumpTable[DUP9] = jumpPtr{makeDup(9), true}
+ jumpTable[DUP10] = jumpPtr{makeDup(10), true}
+ jumpTable[DUP11] = jumpPtr{makeDup(11), true}
+ jumpTable[DUP12] = jumpPtr{makeDup(12), true}
+ jumpTable[DUP13] = jumpPtr{makeDup(13), true}
+ jumpTable[DUP14] = jumpPtr{makeDup(14), true}
+ jumpTable[DUP15] = jumpPtr{makeDup(15), true}
+ jumpTable[DUP16] = jumpPtr{makeDup(16), true}
+
+ jumpTable[RETURN] = jumpPtr{nil, true}
+ jumpTable[SUICIDE] = jumpPtr{nil, true}
+ jumpTable[JUMP] = jumpPtr{nil, true}
+ jumpTable[JUMPI] = jumpPtr{nil, true}
+ jumpTable[STOP] = jumpPtr{nil, true}
+}
diff --git a/core/vm/log.go b/core/vm/log.go
new file mode 100644
index 000000000..191e3a253
--- /dev/null
+++ b/core/vm/log.go
@@ -0,0 +1,71 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// 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.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package vm
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+type Log struct {
+ // Consensus fields
+ Address common.Address
+ Topics []common.Hash
+ Data []byte
+
+ // Derived fields (don't reorder!)
+ BlockNumber uint64
+ TxHash common.Hash
+ TxIndex uint
+ BlockHash common.Hash
+ Index uint
+}
+
+func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
+ return &Log{Address: address, Topics: topics, Data: data, BlockNumber: number}
+}
+
+func (l *Log) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, []interface{}{l.Address, l.Topics, l.Data})
+}
+
+func (l *Log) DecodeRLP(s *rlp.Stream) error {
+ var log struct {
+ Address common.Address
+ Topics []common.Hash
+ Data []byte
+ }
+ if err := s.Decode(&log); err != nil {
+ return err
+ }
+ l.Address, l.Topics, l.Data = log.Address, log.Topics, log.Data
+ return nil
+}
+
+func (l *Log) String() string {
+ return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index)
+}
+
+type Logs []*Log
+
+// LogForStorage is a wrapper around a Log that flattens and parses the entire
+// content of a log, as opposed to only the consensus fields originally (by hiding
+// the rlp interface methods).
+type LogForStorage Log
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 736f595f6..2bd02319f 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
)
+// StdErrFormat formats a slice of StructLogs to human readable format
func StdErrFormat(logs []StructLog) {
fmt.Fprintf(os.Stderr, "VM STAT %d OPs\n", len(logs))
for _, log := range logs {
diff --git a/core/vm/memory.go b/core/vm/memory.go
index 0109050d7..d01188417 100644
--- a/core/vm/memory.go
+++ b/core/vm/memory.go
@@ -18,6 +18,7 @@ package vm
import "fmt"
+// Memory implements a simple memory model for the ethereum virtual machine.
type Memory struct {
store []byte
}
@@ -26,6 +27,7 @@ func NewMemory() *Memory {
return &Memory{nil}
}
+// Set sets offset + size to value
func (m *Memory) Set(offset, size uint64, value []byte) {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
@@ -40,12 +42,14 @@ func (m *Memory) Set(offset, size uint64, value []byte) {
}
}
+// Resize resizes the memory to size
func (m *Memory) Resize(size uint64) {
if uint64(m.Len()) < size {
m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
}
}
+// Get returns offset + size as a new slice
func (self *Memory) Get(offset, size int64) (cpy []byte) {
if size == 0 {
return nil
@@ -61,6 +65,7 @@ func (self *Memory) Get(offset, size int64) (cpy []byte) {
return
}
+// GetPtr returns the offset + size
func (self *Memory) GetPtr(offset, size int64) []byte {
if size == 0 {
return nil
@@ -73,10 +78,12 @@ func (self *Memory) GetPtr(offset, size int64) []byte {
return nil
}
+// Len returns the length of the backing slice
func (m *Memory) Len() int {
return len(m.store)
}
+// Data returns the backing slice
func (m *Memory) Data() []byte {
return m.store
}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index ecced3650..dc4139092 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -20,9 +20,21 @@ import (
"fmt"
)
+// OpCode is an EVM opcode
type OpCode byte
-// Op codes
+func (op OpCode) IsPush() bool {
+ switch op {
+ case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
+ return true
+ }
+ return false
+}
+
+func (op OpCode) IsStaticJump() bool {
+ return op == JUMP
+}
+
const (
// 0x0 range - arithmetic ops
STOP OpCode = iota
@@ -175,6 +187,13 @@ const (
LOG4
)
+// unofficial opcodes used for parsing
+const (
+ PUSH OpCode = 0xb0 + iota
+ DUP
+ SWAP
+)
+
const (
// 0xf0 range - closures
CREATE OpCode = 0xf0 + iota
@@ -182,7 +201,6 @@ const (
CALLCODE
RETURN
- // 0x70 range - other
SUICIDE = 0xff
)
@@ -335,9 +353,11 @@ var opCodeToString = map[OpCode]string{
CALL: "CALL",
RETURN: "RETURN",
CALLCODE: "CALLCODE",
+ SUICIDE: "SUICIDE",
- // 0x70 range - other
- SUICIDE: "SUICIDE",
+ PUSH: "PUSH",
+ DUP: "DUP",
+ SWAP: "SWAP",
}
func (o OpCode) String() string {
diff --git a/core/vm/segments.go b/core/vm/segments.go
new file mode 100644
index 000000000..fd4065149
--- /dev/null
+++ b/core/vm/segments.go
@@ -0,0 +1,44 @@
+package vm
+
+import "math/big"
+
+type jumpSeg struct {
+ pos uint64
+ err error
+ gas *big.Int
+}
+
+func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) {
+ if !contract.UseGas(j.gas) {
+ return nil, OutOfGasError
+ }
+ if j.err != nil {
+ return nil, j.err
+ }
+ *pc = j.pos
+ return nil, nil
+}
+func (s jumpSeg) halts() bool { return false }
+func (s jumpSeg) Op() OpCode { return 0 }
+
+type pushSeg struct {
+ data []*big.Int
+ gas *big.Int
+}
+
+func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) ([]byte, error) {
+ // Use the calculated gas. When insufficient gas is present, use all gas and return an
+ // Out Of Gas error
+ if !contract.UseGas(s.gas) {
+ return nil, OutOfGasError
+ }
+
+ for _, d := range s.data {
+ stack.push(new(big.Int).Set(d))
+ }
+ *pc += uint64(len(s.data))
+ return nil, nil
+}
+
+func (s pushSeg) halts() bool { return false }
+func (s pushSeg) Op() OpCode { return 0 }
diff --git a/core/vm/settings.go b/core/vm/settings.go
deleted file mode 100644
index f9296f6c8..000000000
--- a/core/vm/settings.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// 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.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package vm
-
-var (
- EnableJit bool // Enables the JIT VM
- ForceJit bool // Force the JIT, skip byte VM
- MaxProgSize int // Max cache size for JIT Programs
-)
-
-const defaultJitMaxCache int = 64
diff --git a/core/vm/stack.go b/core/vm/stack.go
index 009ac9e1b..0046edec2 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -42,6 +42,9 @@ func (st *stack) push(d *big.Int) {
//st.data = append(st.data, stackItem)
st.data = append(st.data, d)
}
+func (st *stack) pushN(ds ...*big.Int) {
+ st.data = append(st.data, ds...)
+}
func (st *stack) pop() (ret *big.Int) {
ret = st.data[len(st.data)-1]
diff --git a/core/vm/virtual_machine.go b/core/vm/virtual_machine.go
index 047723744..9b3340bb2 100644
--- a/core/vm/virtual_machine.go
+++ b/core/vm/virtual_machine.go
@@ -16,7 +16,8 @@
package vm
+// VirtualMachine is an EVM interface
type VirtualMachine interface {
Env() Environment
- Run(context *Context, data []byte) ([]byte, error)
+ Run(*Contract, []byte) ([]byte, error)
}
diff --git a/core/vm/vm.go b/core/vm/vm.go
index d9e1a0ce5..4b03e55f0 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -14,33 +14,32 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-// Package vm implements the Ethereum Virtual Machine.
package vm
import (
"fmt"
"math/big"
+ "time"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
)
-// Vm implements VirtualMachine
+// Vm is an EVM and implements VirtualMachine
type Vm struct {
env Environment
}
-// New returns a new Virtual Machine
+// New returns a new Vm
func New(env Environment) *Vm {
return &Vm{env: env}
}
// Run loops and evaluates the contract's code with the given input data
-func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
+func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
self.env.SetDepth(self.env.Depth() + 1)
defer self.env.SetDepth(self.env.Depth() - 1)
@@ -48,42 +47,48 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
defer func() {
if err != nil {
// In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
- context.UseGas(context.Gas)
+ contract.UseGas(contract.Gas)
- ret = context.Return(nil)
+ ret = contract.Return(nil)
}
}()
- if context.CodeAddr != nil {
- if p := Precompiled[context.CodeAddr.Str()]; p != nil {
- return self.RunPrecompiled(p, input, context)
+ if contract.CodeAddr != nil {
+ if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
+ return self.RunPrecompiled(p, input, contract)
}
}
+ // Don't bother with the execution if there's no code.
+ if len(contract.Code) == 0 {
+ return contract.Return(nil), nil
+ }
+
var (
- codehash = crypto.Sha3Hash(context.Code) // codehash is used when doing jump dest caching
+ codehash = crypto.Sha3Hash(contract.Code) // codehash is used when doing jump dest caching
program *Program
)
if EnableJit {
- // Fetch program status.
- // * If ready run using JIT
- // * If unknown, compile in a seperate goroutine
- // * If forced wait for compilation and run once done
- if status := GetProgramStatus(codehash); status == progReady {
- return RunProgram(GetProgram(codehash), self.env, context, input)
- } else if status == progUnknown {
+ // 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:
if ForceJit {
// Create and compile program
- program = NewProgram(context.Code)
+ program = NewProgram(contract.Code)
perr := CompileProgram(program)
if perr == nil {
- return RunProgram(program, self.env, context, input)
+ return RunProgram(program, self.env, contract, input)
}
glog.V(logger.Info).Infoln("error compiling program", err)
} else {
// create and compile the program. Compilation
// is done in a seperate goroutine
- program = NewProgram(context.Code)
+ program = NewProgram(contract.Code)
go func() {
err := CompileProgram(program)
if err != nil {
@@ -96,15 +101,14 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
}
var (
- caller = context.caller
- code = context.Code
- value = context.value
- price = context.Price
-
- op OpCode // current opcode
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
- statedb = self.env.State() // current state
+ 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
// 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
@@ -112,8 +116,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
// 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 {
- if !context.jumpdests.has(codehash, code, to) {
- nop := context.GetOp(to.Uint64())
+ if !contract.jumpdests.has(codehash, code, to) {
+ nop := contract.GetOp(to.Uint64())
return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
}
@@ -125,552 +129,92 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
newMemSize *big.Int
cost *big.Int
)
+ contract.Input = input
// 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 {
- self.log(pc, op, context.Gas, cost, mem, stack, context, err)
+ self.log(pc, op, contract.Gas, cost, mem, stack, contract, err)
}
}()
- // Don't bother with the execution if there's no code.
- if len(code) == 0 {
- return context.Return(nil), nil
+ 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)
+ }()
}
- for {
- // Overhead of the atomic read might not be worth it
- /* TODO this still causes a few issues in the tests
- if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady {
- // move execution
- glog.V(logger.Info).Infoln("Moved execution to JIT")
- return runProgram(program, pc, mem, stack, self.env, context, input)
- }
+ 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)
+ }
+ }
*/
- // The base for all big integer arithmetic
- base := new(big.Int)
// Get the memory location of pc
- op = context.GetOp(pc)
+ op = contract.GetOp(pc)
// calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err = calculateGasAndSize(self.env, context, caller, op, statedb, mem, stack)
+ newMemSize, cost, err = calculateGasAndSize(self.env, contract, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
}
// Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error
- if !context.UseGas(cost) {
+ if !contract.UseGas(cost) {
return nil, OutOfGasError
}
// Resize the memory calculated previously
mem.Resize(newMemSize.Uint64())
// Add a log message
- self.log(pc, op, context.Gas, cost, mem, stack, context, nil)
-
- switch op {
- case ADD:
- x, y := stack.pop(), stack.pop()
-
- base.Add(x, y)
-
- U256(base)
-
- // pop result back on the stack
- stack.push(base)
- case SUB:
- x, y := stack.pop(), stack.pop()
-
- base.Sub(x, y)
-
- U256(base)
-
- // pop result back on the stack
- stack.push(base)
- case MUL:
- x, y := stack.pop(), stack.pop()
-
- base.Mul(x, y)
-
- U256(base)
-
- // pop result back on the stack
- stack.push(base)
- case DIV:
- x, y := stack.pop(), stack.pop()
-
- if y.Cmp(common.Big0) != 0 {
- base.Div(x, y)
- }
-
- U256(base)
-
- // pop result back on the stack
- stack.push(base)
- case SDIV:
- x, y := S256(stack.pop()), S256(stack.pop())
-
- if y.Cmp(common.Big0) == 0 {
- base.Set(common.Big0)
- } else {
- n := new(big.Int)
- if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
- n.SetInt64(-1)
- } else {
- n.SetInt64(1)
- }
-
- base.Div(x.Abs(x), y.Abs(y)).Mul(base, n)
-
- U256(base)
- }
-
- stack.push(base)
- case MOD:
- x, y := stack.pop(), stack.pop()
-
- if y.Cmp(common.Big0) == 0 {
- base.Set(common.Big0)
- } else {
- base.Mod(x, y)
- }
-
- U256(base)
-
- stack.push(base)
- case SMOD:
- x, y := S256(stack.pop()), S256(stack.pop())
-
- if y.Cmp(common.Big0) == 0 {
- base.Set(common.Big0)
- } else {
- n := new(big.Int)
- if x.Cmp(common.Big0) < 0 {
- n.SetInt64(-1)
- } else {
- n.SetInt64(1)
- }
-
- base.Mod(x.Abs(x), y.Abs(y)).Mul(base, n)
-
- U256(base)
- }
-
- stack.push(base)
-
- case EXP:
- x, y := stack.pop(), stack.pop()
-
- base.Exp(x, y, Pow256)
-
- U256(base)
-
- stack.push(base)
- case SIGNEXTEND:
- back := stack.pop()
- if back.Cmp(big.NewInt(31)) < 0 {
- bit := uint(back.Uint64()*8 + 7)
- num := stack.pop()
- mask := new(big.Int).Lsh(common.Big1, bit)
- mask.Sub(mask, common.Big1)
- if common.BitTest(num, int(bit)) {
- num.Or(num, mask.Not(mask))
- } else {
- num.And(num, mask)
- }
-
- num = U256(num)
-
- stack.push(num)
- }
- case NOT:
- stack.push(U256(new(big.Int).Not(stack.pop())))
- case LT:
- x, y := stack.pop(), stack.pop()
-
- // x < y
- if x.Cmp(y) < 0 {
- stack.push(common.BigTrue)
- } else {
- stack.push(common.BigFalse)
- }
- case GT:
- x, y := stack.pop(), stack.pop()
-
- // x > y
- if x.Cmp(y) > 0 {
- stack.push(common.BigTrue)
- } else {
- stack.push(common.BigFalse)
- }
-
- case SLT:
- x, y := S256(stack.pop()), S256(stack.pop())
-
- // x < y
- if x.Cmp(S256(y)) < 0 {
- stack.push(common.BigTrue)
- } else {
- stack.push(common.BigFalse)
- }
- case SGT:
- x, y := S256(stack.pop()), S256(stack.pop())
-
- // x > y
- if x.Cmp(y) > 0 {
- stack.push(common.BigTrue)
- } else {
- stack.push(common.BigFalse)
- }
-
- case EQ:
- x, y := stack.pop(), stack.pop()
-
- // x == y
- if x.Cmp(y) == 0 {
- stack.push(common.BigTrue)
- } else {
- stack.push(common.BigFalse)
- }
- case ISZERO:
- x := stack.pop()
- if x.Cmp(common.BigFalse) > 0 {
- stack.push(common.BigFalse)
- } else {
- stack.push(common.BigTrue)
- }
-
- case AND:
- x, y := stack.pop(), stack.pop()
-
- stack.push(base.And(x, y))
- case OR:
- x, y := stack.pop(), stack.pop()
-
- stack.push(base.Or(x, y))
- case XOR:
- x, y := stack.pop(), stack.pop()
-
- stack.push(base.Xor(x, y))
- case BYTE:
- th, val := stack.pop(), stack.pop()
-
- if th.Cmp(big.NewInt(32)) < 0 {
- byt := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
-
- base.Set(byt)
- } else {
- base.Set(common.BigFalse)
- }
-
- stack.push(base)
- case ADDMOD:
- x := stack.pop()
- y := stack.pop()
- z := stack.pop()
-
- if z.Cmp(Zero) > 0 {
- add := new(big.Int).Add(x, y)
- base.Mod(add, z)
-
- base = U256(base)
- }
-
- stack.push(base)
- case MULMOD:
- x := stack.pop()
- y := stack.pop()
- z := stack.pop()
-
- if z.Cmp(Zero) > 0 {
- mul := new(big.Int).Mul(x, y)
- base.Mod(mul, z)
-
- U256(base)
- }
-
- stack.push(base)
-
- case SHA3:
- offset, size := stack.pop(), stack.pop()
- data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64()))
-
- stack.push(common.BigD(data))
-
- case ADDRESS:
- stack.push(common.Bytes2Big(context.Address().Bytes()))
-
- case BALANCE:
- addr := common.BigToAddress(stack.pop())
- balance := statedb.GetBalance(addr)
+ self.log(pc, op, contract.Gas, cost, mem, stack, contract, nil)
- stack.push(new(big.Int).Set(balance))
-
- case ORIGIN:
- origin := self.env.Origin()
-
- stack.push(origin.Big())
-
- case CALLER:
- caller := context.caller.Address()
- stack.push(common.Bytes2Big(caller.Bytes()))
-
- case CALLVALUE:
- stack.push(new(big.Int).Set(value))
-
- case CALLDATALOAD:
- data := getData(input, stack.pop(), common.Big32)
-
- stack.push(common.Bytes2Big(data))
- case CALLDATASIZE:
- l := int64(len(input))
- stack.push(big.NewInt(l))
-
- case CALLDATACOPY:
- var (
- mOff = stack.pop()
- cOff = stack.pop()
- l = stack.pop()
- )
- data := getData(input, cOff, l)
-
- mem.Set(mOff.Uint64(), l.Uint64(), data)
-
- case CODESIZE, EXTCODESIZE:
- var code []byte
- if op == EXTCODESIZE {
- addr := common.BigToAddress(stack.pop())
-
- code = statedb.GetCode(addr)
- } else {
- code = context.Code
- }
-
- l := big.NewInt(int64(len(code)))
- stack.push(l)
-
- case CODECOPY, EXTCODECOPY:
- var code []byte
- if op == EXTCODECOPY {
- addr := common.BigToAddress(stack.pop())
- code = statedb.GetCode(addr)
- } else {
- code = context.Code
- }
-
- var (
- mOff = stack.pop()
- cOff = stack.pop()
- l = stack.pop()
- )
-
- codeCopy := getData(code, cOff, l)
-
- mem.Set(mOff.Uint64(), l.Uint64(), codeCopy)
-
- case GASPRICE:
- stack.push(new(big.Int).Set(context.Price))
-
- case BLOCKHASH:
- num := stack.pop()
-
- n := new(big.Int).Sub(self.env.BlockNumber(), common.Big257)
- if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 {
- stack.push(self.env.GetHash(num.Uint64()).Big())
+ if opPtr := jumpTable[op]; opPtr.valid {
+ if opPtr.fn != nil {
+ opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack)
} else {
- stack.push(common.Big0)
- }
-
- case COINBASE:
- coinbase := self.env.Coinbase()
-
- stack.push(coinbase.Big())
-
- case TIMESTAMP:
- time := self.env.Time()
-
- stack.push(new(big.Int).Set(time))
-
- case NUMBER:
- number := self.env.BlockNumber()
-
- stack.push(U256(number))
-
- case DIFFICULTY:
- difficulty := self.env.Difficulty()
-
- stack.push(new(big.Int).Set(difficulty))
-
- case GASLIMIT:
-
- stack.push(new(big.Int).Set(self.env.GasLimit()))
-
- case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
- size := uint64(op - PUSH1 + 1)
- byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size))
- // push value to stack
- stack.push(common.Bytes2Big(byts))
- pc += size
-
- case POP:
- stack.pop()
- case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
- n := int(op - DUP1 + 1)
- stack.dup(n)
-
- case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
- n := int(op - SWAP1 + 2)
- stack.swap(n)
-
- case LOG0, LOG1, LOG2, LOG3, LOG4:
- n := int(op - LOG0)
- topics := make([]common.Hash, n)
- mStart, mSize := stack.pop(), stack.pop()
- for i := 0; i < n; i++ {
- topics[i] = common.BigToHash(stack.pop())
- }
-
- data := mem.Get(mStart.Int64(), mSize.Int64())
- log := state.NewLog(context.Address(), topics, data, self.env.BlockNumber().Uint64())
- self.env.AddLog(log)
-
- case MLOAD:
- offset := stack.pop()
- val := common.BigD(mem.Get(offset.Int64(), 32))
- stack.push(val)
-
- case MSTORE:
- // pop value of the stack
- mStart, val := stack.pop(), stack.pop()
- mem.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
-
- case MSTORE8:
- off, val := stack.pop().Int64(), stack.pop().Int64()
-
- mem.store[off] = byte(val & 0xff)
-
- case SLOAD:
- loc := common.BigToHash(stack.pop())
- val := statedb.GetState(context.Address(), loc).Big()
- stack.push(val)
-
- case SSTORE:
- loc := common.BigToHash(stack.pop())
- val := stack.pop()
-
- statedb.SetState(context.Address(), loc, common.BigToHash(val))
-
- case JUMP:
- if err := jump(pc, stack.pop()); err != nil {
- return nil, err
- }
+ 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
+ }
- continue
- case JUMPI:
- pos, cond := stack.pop(), stack.pop()
+ continue
+ case JUMPI:
+ pos, cond := stack.pop(), stack.pop()
- if cond.Cmp(common.BigTrue) >= 0 {
- if err := jump(pc, pos); err != nil {
- return nil, err
- }
+ if cond.Cmp(common.BigTrue) >= 0 {
+ if err := jump(pc, pos); err != nil {
+ return nil, err
+ }
- continue
- }
+ continue
+ }
+ case RETURN:
+ offset, size := stack.pop(), stack.pop()
+ ret := mem.GetPtr(offset.Int64(), size.Int64())
- case JUMPDEST:
- case PC:
- stack.push(new(big.Int).SetUint64(pc))
- case MSIZE:
- stack.push(big.NewInt(int64(mem.Len())))
- case GAS:
- stack.push(new(big.Int).Set(context.Gas))
- case CREATE:
-
- var (
- value = stack.pop()
- offset, size = stack.pop(), stack.pop()
- input = mem.Get(offset.Int64(), size.Int64())
- gas = new(big.Int).Set(context.Gas)
- addr common.Address
- )
-
- context.UseGas(context.Gas)
- ret, suberr, ref := self.env.Create(context, input, gas, price, value)
- if suberr != nil {
- stack.push(common.BigFalse)
+ return contract.Return(ret), nil
+ case SUICIDE:
+ opSuicide(instruction{}, nil, self.env, contract, mem, stack)
- } else {
- // gas < len(ret) * CreateDataGas == NO_CODE
- dataGas := big.NewInt(int64(len(ret)))
- dataGas.Mul(dataGas, params.CreateDataGas)
- if context.UseGas(dataGas) {
- ref.SetCode(ret)
+ fallthrough
+ case STOP: // Stop the contract
+ return contract.Return(nil), nil
}
- addr = ref.Address()
-
- stack.push(addr.Big())
-
}
-
- case CALL, CALLCODE:
- gas := stack.pop()
- // pop gas and value of the stack.
- addr, value := stack.pop(), stack.pop()
- value = U256(value)
- // pop input size and offset
- inOffset, inSize := stack.pop(), stack.pop()
- // pop return size and offset
- retOffset, retSize := stack.pop(), stack.pop()
-
- address := common.BigToAddress(addr)
-
- // Get the arguments from the memory
- args := mem.Get(inOffset.Int64(), inSize.Int64())
-
- if len(value.Bytes()) > 0 {
- gas.Add(gas, params.CallStipend)
- }
-
- var (
- ret []byte
- err error
- )
- if op == CALLCODE {
- ret, err = self.env.CallCode(context, address, args, gas, price, value)
- } else {
- ret, err = self.env.Call(context, address, args, gas, price, value)
- }
-
- if err != nil {
- stack.push(common.BigFalse)
-
- } else {
- stack.push(common.BigTrue)
-
- mem.Set(retOffset.Uint64(), retSize.Uint64(), ret)
- }
-
- case RETURN:
- offset, size := stack.pop(), stack.pop()
- ret := mem.GetPtr(offset.Int64(), size.Int64())
-
- return context.Return(ret), nil
- case SUICIDE:
- receiver := statedb.GetOrNewStateObject(common.BigToAddress(stack.pop()))
- balance := statedb.GetBalance(context.Address())
-
- receiver.AddBalance(balance)
-
- statedb.Delete(context.Address())
-
- fallthrough
- case STOP: // Stop the context
-
- return context.Return(nil), nil
- default:
-
+ } else {
return nil, fmt.Errorf("Invalid opcode %x", op)
}
@@ -681,7 +225,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
// 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.
-func calculateGasAndSize(env Environment, context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
+func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -731,7 +275,7 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
- val := statedb.GetState(context.Address(), common.BigToHash(x))
+ val := statedb.GetState(contract.Address(), common.BigToHash(x))
// This checks for 3 scenario's and calculates gas accordingly
// 1. From a zero-value address to a non-zero value (NEW VALUE)
@@ -741,7 +285,7 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
// 0 => non 0
g = params.SstoreSetGas
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- statedb.Refund(params.SstoreRefundGas)
+ statedb.AddRefund(params.SstoreRefundGas)
g = params.SstoreClearGas
} else {
@@ -750,8 +294,8 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
}
gas.Set(g)
case SUICIDE:
- if !statedb.IsDeleted(context.Address()) {
- statedb.Refund(params.SuicideRefundGas)
+ if !statedb.IsDeleted(contract.Address()) {
+ statedb.AddRefund(params.SuicideRefundGas)
}
case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32))
@@ -788,7 +332,8 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
gas.Add(gas, stack.data[stack.len()-1])
if op == CALL {
- if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
+ //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
+ if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
gas.Add(gas, params.CallNewAccountGas)
}
}
@@ -802,38 +347,18 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
newMemSize = common.BigMax(x, y)
}
-
- if newMemSize.Cmp(common.Big0) > 0 {
- newMemSizeWords := toWordSize(newMemSize)
- newMemSize.Mul(newMemSizeWords, u256(32))
-
- if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
- oldSize := toWordSize(big.NewInt(int64(mem.Len())))
- pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
- linCoef := new(big.Int).Mul(oldSize, params.MemoryGas)
- quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
- oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
-
- pow.Exp(newMemSizeWords, common.Big2, Zero)
- linCoef = new(big.Int).Mul(newMemSizeWords, params.MemoryGas)
- quadCoef = new(big.Int).Div(pow, params.QuadCoeffDiv)
- newTotalFee := new(big.Int).Add(linCoef, quadCoef)
-
- fee := new(big.Int).Sub(newTotalFee, oldTotalFee)
- gas.Add(gas, fee)
- }
- }
+ quadMemGas(mem, newMemSize, gas)
return newMemSize, gas, nil
}
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
-func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Context) (ret []byte, err error) {
+func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.Gas(len(input))
- if context.UseGas(gas) {
+ if contract.UseGas(gas) {
ret = p.Call(input)
- return context.Return(ret), nil
+ return contract.Return(ret), nil
} else {
return nil, OutOfGasError
}
@@ -841,19 +366,22 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Con
// log emits a log event to the environment for each opcode encountered. This is not to be confused with the
// LOG* opcode.
-func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, context *Context, err error) {
+func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) {
if Debug {
mem := make([]byte, len(memory.Data()))
copy(mem, memory.Data())
- stck := make([]*big.Int, len(stack.Data()))
- copy(stck, stack.Data())
- object := context.self.(*state.StateObject)
+ stck := make([]*big.Int, len(stack.Data()))
+ for i, item := range stack.Data() {
+ stck[i] = new(big.Int).Set(item)
+ }
storage := make(map[common.Hash][]byte)
- object.EachStorage(func(k, v []byte) {
- storage[common.BytesToHash(k)] = v
- })
-
+ /*
+ object := contract.self.(*state.StateObject)
+ object.EachStorage(func(k, v []byte) {
+ storage[common.BytesToHash(k)] = v
+ })
+ */
self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, err})
}
}
diff --git a/core/vm/vm_jit.go b/core/vm/vm_jit.go
index 339cb8ea8..07cb52d4a 100644
--- a/core/vm/vm_jit.go
+++ b/core/vm/vm_jit.go
@@ -30,6 +30,7 @@ void evmjit_destroy(void* _jit);
*/
import "C"
+/*
import (
"bytes"
"errors"
@@ -385,4 +386,4 @@ func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte
code := vm.Env().State().GetCode(addr)
*o_size = uint64(len(code))
return getDataPtr(code)
-}
+}*/