aboutsummaryrefslogtreecommitdiffstats
path: root/core/vm
diff options
context:
space:
mode:
Diffstat (limited to 'core/vm')
-rw-r--r--core/vm/contract.go13
-rw-r--r--core/vm/environment.go363
-rw-r--r--core/vm/errors.go11
-rw-r--r--core/vm/instructions.go202
-rw-r--r--core/vm/interface.go97
-rw-r--r--core/vm/jit.go18
-rw-r--r--core/vm/jit_test.go66
-rw-r--r--core/vm/logger.go8
-rw-r--r--core/vm/logger_test.go29
-rw-r--r--core/vm/noop.go68
-rw-r--r--core/vm/runtime/env.go98
-rw-r--r--core/vm/runtime/runtime.go29
-rw-r--r--core/vm/segments.go4
-rw-r--r--core/vm/util_test.go32
-rw-r--r--core/vm/vm.go60
-rw-r--r--core/vm/vm_jit_fake.go7
16 files changed, 661 insertions, 444 deletions
diff --git a/core/vm/contract.go b/core/vm/contract.go
index 70455a4c2..dfa93ab18 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -24,7 +24,7 @@ import (
// ContractRef is a reference to the contract's backing object
type ContractRef interface {
- ReturnGas(*big.Int, *big.Int)
+ ReturnGas(*big.Int)
Address() common.Address
Value() *big.Int
SetCode(common.Hash, []byte)
@@ -48,7 +48,7 @@ type Contract struct {
CodeAddr *common.Address
Input []byte
- value, Gas, UsedGas, Price *big.Int
+ value, Gas, UsedGas *big.Int
Args []byte
@@ -56,7 +56,7 @@ type Contract struct {
}
// NewContract returns a new contract environment for the execution of EVM.
-func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
+func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
if parent, ok := caller.(*Contract); ok {
@@ -70,9 +70,6 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.
// This pointer will be off the state transition
c.Gas = gas //new(big.Int).Set(gas)
c.value = new(big.Int).Set(value)
- // In most cases price and value are pointers to transaction objects
- // and we don't want the transaction's values to change.
- c.Price = new(big.Int).Set(price)
c.UsedGas = new(big.Int)
return c
@@ -114,7 +111,7 @@ func (c *Contract) Caller() common.Address {
// caller.
func (c *Contract) Finalise() {
// Return the remaining gas to the caller
- c.caller.ReturnGas(c.Gas, c.Price)
+ c.caller.ReturnGas(c.Gas)
}
// UseGas attempts the use gas and subtracts it and returns true on success
@@ -127,7 +124,7 @@ func (c *Contract) UseGas(gas *big.Int) (ok bool) {
}
// ReturnGas adds the given gas back to itself.
-func (c *Contract) ReturnGas(gas, price *big.Int) {
+func (c *Contract) ReturnGas(gas *big.Int) {
// Return the gas to the context
c.Gas.Add(c.Gas, gas)
c.UsedGas.Sub(c.UsedGas, gas)
diff --git a/core/vm/environment.go b/core/vm/environment.go
index e97c1e58c..50a09d444 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -17,110 +17,299 @@
package vm
import (
+ "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
-// Environment is an EVM requirement and helper which allows access to outside
-// information such as states.
-type Environment interface {
- // The current ruleset
- ChainConfig() *params.ChainConfig
- // The state database
- Db() Database
- // Creates a restorable snapshot
- SnapshotDatabase() int
- // Set database to previous snapshot
- RevertToSnapshot(int)
- // Address of the original invoker (first occurrence of the VM invoker)
- Origin() common.Address
- // The block number this VM is invoked on
- BlockNumber() *big.Int
- // 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
- // 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)
- // Type of the VM
- Vm() Vm
- // Get the curret calling depth
- Depth() int
- // Set the current calling depth
- SetDepth(i int)
- // 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)
- // Same as CallCode except sender and value is propagated from parent to child scope
- DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error)
- // Create a new contract
- Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
+type (
+ CanTransferFunc func(StateDB, common.Address, *big.Int) bool
+ TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
+ // GetHashFunc returns the nth block hash in the blockchain
+ // and is used by the BLOCKHASH EVM op code.
+ GetHashFunc func(uint64) common.Hash
+)
+
+// Context provides the EVM with auxilary information. Once provided it shouldn't be modified.
+type Context struct {
+ // CanTransfer returns whether the account contains
+ // sufficient ether to transfer the value
+ CanTransfer CanTransferFunc
+ // Transfer transfers ether from one account to the other
+ Transfer TransferFunc
+ // GetHash returns the hash corresponding to n
+ GetHash GetHashFunc
+
+ // Message information
+ Origin common.Address // Provides information for ORIGIN
+ GasPrice *big.Int // Provides information for GASPRICE
+
+ // Block information
+ Coinbase common.Address // Provides information for COINBASE
+ GasLimit *big.Int // Provides information for GASLIMIT
+ BlockNumber *big.Int // Provides information for NUMBER
+ Time *big.Int // Provides information for TIME
+ Difficulty *big.Int // Provides information for DIFFICULTY
+}
+
+// Environment provides information about external sources for the EVM
+//
+// The Environment should never be reused and is not thread safe.
+type Environment struct {
+ // Context provides auxiliary blockchain related information
+ Context
+ // StateDB gives access to the underlying state
+ StateDB StateDB
+ // Depth is the current call stack
+ Depth int
+
+ // evm is the ethereum virtual machine
+ evm Vm
+ // chainConfig contains information about the current chain
+ chainConfig *params.ChainConfig
+ vmConfig Config
+}
+
+// NewEnvironment retutrns a new EVM environment.
+func NewEnvironment(context Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *Environment {
+ env := &Environment{
+ Context: context,
+ StateDB: statedb,
+ vmConfig: vmConfig,
+ chainConfig: chainConfig,
+ }
+ env.evm = New(env, vmConfig)
+ return env
+}
+
+// Call executes the contract associated with the addr with the given input as paramaters. It also handles any
+// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
+// case of an execution error or failed value transfer.
+func (env *Environment) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) {
+ if env.vmConfig.NoRecursion && env.Depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, DepthError
+ }
+ if !env.Context.CanTransfer(env.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, ErrInsufficientBalance
+ }
+
+ var (
+ to Account
+ snapshotPreTransfer = env.StateDB.Snapshot()
+ )
+ if !env.StateDB.Exist(addr) {
+ if Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber) && value.BitLen() == 0 {
+ caller.ReturnGas(gas)
+ return nil, nil
+ }
+
+ to = env.StateDB.CreateAccount(addr)
+ } else {
+ to = env.StateDB.GetAccount(addr)
+ }
+ env.Transfer(env.StateDB, caller.Address(), to.Address(), value)
+
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped environment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
+ defer contract.Finalise()
+
+ ret, err := env.EVM().Run(contract, input)
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if err != nil {
+ contract.UseGas(contract.Gas)
+
+ env.StateDB.RevertToSnapshot(snapshotPreTransfer)
+ }
+ return ret, err
}
-// Vm is the basic interface for an implementation of the EVM.
-type Vm interface {
- // Run should execute the given contract with the input given in in
- // and return the contract execution return bytes or an error if it
- // failed.
- Run(c *Contract, in []byte) ([]byte, error)
+// CallCode executes the contract associated with the addr with the given input as paramaters. It also handles any
+// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
+// case of an execution error or failed value transfer.
+//
+// CallCode differs from Call in the sense that it executes the given address' code with the caller as context.
+func (env *Environment) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) {
+ if env.vmConfig.NoRecursion && env.Depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, DepthError
+ }
+ if !env.CanTransfer(env.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, env.StateDB.GetBalance(caller.Address()))
+ }
+
+ var (
+ snapshotPreTransfer = env.StateDB.Snapshot()
+ to = env.StateDB.GetAccount(caller.Address())
+ )
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped environment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
+ defer contract.Finalise()
+
+ ret, err := env.EVM().Run(contract, input)
+ if err != nil {
+ contract.UseGas(contract.Gas)
+
+ env.StateDB.RevertToSnapshot(snapshotPreTransfer)
+ }
+
+ return ret, err
}
-// Database is a EVM database for full state querying.
-type Database interface {
- GetAccount(common.Address) Account
- CreateAccount(common.Address) Account
+// DelegateCall executes the contract associated with the addr with the given input as paramaters.
+// It reverses the state in case of an execution error.
+//
+// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context
+// and the caller is set to the caller of the caller.
+func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) ([]byte, error) {
+ if env.vmConfig.NoRecursion && env.Depth > 0 {
+ caller.ReturnGas(gas)
- AddBalance(common.Address, *big.Int)
- GetBalance(common.Address) *big.Int
+ return nil, nil
+ }
- GetNonce(common.Address) uint64
- SetNonce(common.Address, uint64)
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+ return nil, DepthError
+ }
- GetCodeHash(common.Address) common.Hash
- GetCodeSize(common.Address) int
- GetCode(common.Address) []byte
- SetCode(common.Address, []byte)
+ var (
+ snapshot = env.StateDB.Snapshot()
+ to = env.StateDB.GetAccount(caller.Address())
+ )
- AddRefund(*big.Int)
- GetRefund() *big.Int
+ // Iinitialise a new contract and make initialise the delegate values
+ contract := NewContract(caller, to, caller.Value(), gas).AsDelegate()
+ contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
+ defer contract.Finalise()
- GetState(common.Address, common.Hash) common.Hash
- SetState(common.Address, common.Hash, common.Hash)
+ ret, err := env.EVM().Run(contract, input)
+ if err != nil {
+ contract.UseGas(contract.Gas)
- Suicide(common.Address) bool
- HasSuicided(common.Address) bool
+ env.StateDB.RevertToSnapshot(snapshot)
+ }
- // Exist reports whether the given account exists in state.
- // Notably this should also return true for suicided accounts.
- Exist(common.Address) bool
- // Empty returns whether the given account is empty. Empty
- // is defined according to EIP161 (balance = nonce = code = 0).
- Empty(common.Address) bool
+ return ret, err
}
-// Account represents a contract or basic ethereum account.
-type Account interface {
- SubBalance(amount *big.Int)
- AddBalance(amount *big.Int)
- SetBalance(*big.Int)
- SetNonce(uint64)
- Balance() *big.Int
- Address() common.Address
- ReturnGas(*big.Int, *big.Int)
- SetCode(common.Hash, []byte)
- ForEachStorage(cb func(key, value common.Hash) bool)
- Value() *big.Int
+// Create creates a new contract using code as deployment code.
+func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.Int) ([]byte, common.Address, error) {
+ if env.vmConfig.NoRecursion && env.Depth > 0 {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, nil
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, DepthError
+ }
+ if !env.CanTransfer(env.StateDB, caller.Address(), value) {
+ caller.ReturnGas(gas)
+
+ return nil, common.Address{}, ErrInsufficientBalance
+ }
+
+ // Create a new account on the state
+ nonce := env.StateDB.GetNonce(caller.Address())
+ env.StateDB.SetNonce(caller.Address(), nonce+1)
+
+ snapshotPreTransfer := env.StateDB.Snapshot()
+ var (
+ addr = crypto.CreateAddress(caller.Address(), nonce)
+ to = env.StateDB.CreateAccount(addr)
+ )
+ if env.ChainConfig().IsEIP158(env.BlockNumber) {
+ env.StateDB.SetNonce(addr, 1)
+ }
+ env.Transfer(env.StateDB, caller.Address(), to.Address(), value)
+
+ // initialise a new contract and set the code that is to be used by the
+ // E The contract is a scoped environment for this execution context
+ // only.
+ contract := NewContract(caller, to, value, gas)
+ contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code)
+ defer contract.Finalise()
+
+ ret, err := env.EVM().Run(contract, nil)
+ // check whether the max code size has been exceeded
+ maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
+ // if the contract creation ran successfully and no errors were returned
+ // calculate the gas required to store the code. If the code could not
+ // be stored due to not enough gas set an error and let it be handled
+ // by the error checking condition below.
+ if err == nil && !maxCodeSizeExceeded {
+ dataGas := big.NewInt(int64(len(ret)))
+ dataGas.Mul(dataGas, params.CreateDataGas)
+ if contract.UseGas(dataGas) {
+ env.StateDB.SetCode(addr, ret)
+ } else {
+ err = CodeStoreOutOfGasError
+ }
+ }
+
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if maxCodeSizeExceeded ||
+ (err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != CodeStoreOutOfGasError)) {
+ contract.UseGas(contract.Gas)
+ env.StateDB.RevertToSnapshot(snapshotPreTransfer)
+
+ // Nothing should be returned when an error is thrown.
+ return nil, addr, err
+ }
+ // If the vm returned with an error the return value should be set to nil.
+ // This isn't consensus critical but merely to for behaviour reasons such as
+ // tests, RPC calls, etc.
+ if err != nil {
+ ret = nil
+ }
+
+ return ret, addr, err
}
+
+// ChainConfig returns the environment's chain configuration
+func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig }
+
+// EVM returns the environments EVM
+func (env *Environment) EVM() Vm { return env.evm }
diff --git a/core/vm/errors.go b/core/vm/errors.go
index 1766bf9fb..f8d26b1f0 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -23,7 +23,10 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-var OutOfGasError = errors.New("Out of gas")
-var CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
-var DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
-var TraceLimitReachedError = errors.New("The number of logs reached the specified limit")
+var (
+ OutOfGasError = errors.New("Out of gas")
+ CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
+ DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
+ TraceLimitReachedError = errors.New("The number of logs reached the specified limit")
+ ErrInsufficientBalance = errors.New("insufficient balance for transfer")
+)
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 4f98953b5..871c09e83 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -28,14 +28,14 @@ import (
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)
+ 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 instrFn func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack)
type instruction struct {
op OpCode
@@ -59,9 +59,9 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract
return mapping[to.Uint64()], nil
}
-func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+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)
+ newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, memory, stack)
if err != nil {
return nil, err
}
@@ -115,26 +115,26 @@ func (instr instruction) Op() OpCode {
return instr.op
}
-func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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)))
@@ -143,7 +143,7 @@ func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
}
-func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, 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))
@@ -163,7 +163,7 @@ func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract,
}
}
-func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, 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))
@@ -172,7 +172,7 @@ func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
}
-func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, 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 {
@@ -192,12 +192,12 @@ func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract,
}
}
-func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opExp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
base, exponent := stack.pop(), stack.pop()
stack.push(math.Exp(base, exponent))
}
-func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, 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)
@@ -214,12 +214,12 @@ func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Cont
}
}
-func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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))
@@ -228,7 +228,7 @@ func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, me
}
}
-func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, 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))
@@ -237,7 +237,7 @@ func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, me
}
}
-func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, 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))
@@ -246,7 +246,7 @@ func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
}
-func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, 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))
@@ -255,7 +255,7 @@ func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
}
-func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, 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))
@@ -264,7 +264,7 @@ func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, me
}
}
-func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, 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))
@@ -273,19 +273,19 @@ func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract
}
}
-func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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()]))
@@ -294,7 +294,7 @@ func opByte(instr instruction, pc *uint64, env Environment, contract *Contract,
stack.push(new(big.Int))
}
}
-func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, 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)
@@ -304,7 +304,7 @@ func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract
stack.push(new(big.Int))
}
}
-func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, 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)
@@ -315,45 +315,45 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract
}
}
-func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, 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.Keccak256(memory.Get(offset.Int64(), size.Int64()))
stack.push(common.BytesToBig(hash))
}
-func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+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, pc *uint64, env Environment, contract *Contract, 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.Db().GetBalance(addr)
+ balance := env.StateDB.GetBalance(addr)
stack.push(new(big.Int).Set(balance))
}
-func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(env.Origin().Big())
+func opOrigin(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(env.Origin.Big())
}
-func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opCaller(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.push(contract.Caller().Big())
}
-func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+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, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+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, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+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, pc *uint64, env Environment, contract *Contract, 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()
@@ -362,18 +362,18 @@ func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Co
memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
}
-func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, 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(env.Db().GetCodeSize(addr)))
+ l := big.NewInt(int64(env.StateDB.GetCodeSize(addr)))
stack.push(l)
}
-func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+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, pc *uint64, env Environment, contract *Contract, 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()
@@ -384,70 +384,70 @@ func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contra
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
}
-func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, 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.Db().GetCode(addr), cOff, l)
+ codeCopy := getData(env.StateDB.GetCode(addr), cOff, l)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
}
-func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(new(big.Int).Set(contract.Price))
+func opGasprice(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(new(big.Int).Set(env.GasPrice))
}
-func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, 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)
- if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber()) < 0 {
+ n := new(big.Int).Sub(env.BlockNumber, common.Big257)
+ if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 {
stack.push(env.GetHash(num.Uint64()).Big())
} else {
stack.push(new(big.Int))
}
}
-func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(env.Coinbase().Big())
+func opCoinbase(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ stack.push(env.Coinbase.Big())
}
-func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.Time())))
+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, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.BlockNumber())))
+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, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.Difficulty())))
+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, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
- stack.push(U256(new(big.Int).Set(env.GasLimit())))
+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, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opPop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.pop()
}
-func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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()
@@ -456,77 +456,77 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
d := memory.Get(mStart.Int64(), mSize.Int64())
- log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
- env.AddLog(log)
+ log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
+ env.StateDB.AddLog(log)
}
-func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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.Db().GetState(contract.Address(), loc).Big()
+ val := env.StateDB.GetState(contract.Address(), loc).Big()
stack.push(val)
}
-func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, 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.Db().SetState(contract.Address(), loc, common.BigToHash(val))
+ env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
}
-func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, 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 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 opJumpdest(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
}
-func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, 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, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+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, pc *uint64, env Environment, contract *Contract, 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(contract.Gas)
)
- if env.ChainConfig().IsEIP150(env.BlockNumber()) {
+ if env.ChainConfig().IsEIP150(env.BlockNumber) {
gas.Div(gas, n64)
gas = gas.Sub(contract.Gas, gas)
}
contract.UseGas(gas)
- _, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
+ _, addr, suberr := env.Create(contract, input, gas, value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
- if env.ChainConfig().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
+ if env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == CodeStoreOutOfGasError {
stack.push(new(big.Int))
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
stack.push(new(big.Int))
@@ -535,7 +535,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
}
}
-func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, 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()
@@ -554,7 +554,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
gas.Add(gas, params.CallStipend)
}
- ret, err := env.Call(contract, address, args, gas, contract.Price, value)
+ ret, err := env.Call(contract, address, args, gas, value)
if err != nil {
stack.push(new(big.Int))
@@ -566,7 +566,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
}
}
-func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, 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()
@@ -585,7 +585,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
gas.Add(gas, params.CallStipend)
}
- ret, err := env.CallCode(contract, address, args, gas, contract.Price, value)
+ ret, err := env.CallCode(contract, address, args, gas, value)
if err != nil {
stack.push(new(big.Int))
@@ -597,12 +597,12 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
}
}
-func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+func opDelegateCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(to)
args := memory.Get(inOffset.Int64(), inSize.Int64())
- ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price)
+ ret, err := env.DelegateCall(contract, toAddr, args, gas)
if err != nil {
stack.push(new(big.Int))
} else {
@@ -611,23 +611,23 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co
}
}
-func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, 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 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, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
+ balance := env.StateDB.GetBalance(contract.Address())
+ env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
- env.Db().Suicide(contract.Address())
+ env.StateDB.Suicide(contract.Address())
}
// 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) {
+ 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++ {
@@ -635,14 +635,14 @@ func makeLog(size int) instrFn {
}
d := memory.Get(mStart.Int64(), mSize.Int64())
- log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
- env.AddLog(log)
+ log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
+ env.StateDB.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) {
+ 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
@@ -651,7 +651,7 @@ func makePush(size uint64, bsize *big.Int) instrFn {
// make push instruction function
func makeDup(size int64) instrFn {
- return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
+ return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.dup(int(size))
}
}
@@ -660,7 +660,7 @@ func makeDup(size int64) instrFn {
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) {
+ return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
stack.swap(int(size))
}
}
diff --git a/core/vm/interface.go b/core/vm/interface.go
new file mode 100644
index 000000000..918fde85f
--- /dev/null
+++ b/core/vm/interface.go
@@ -0,0 +1,97 @@
+// 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 (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// Vm is the basic interface for an implementation of the EVM.
+type Vm interface {
+ // Run should execute the given contract with the input given in in
+ // and return the contract execution return bytes or an error if it
+ // failed.
+ Run(c *Contract, in []byte) ([]byte, error)
+}
+
+// StateDB is an EVM database for full state querying.
+type StateDB interface {
+ GetAccount(common.Address) Account
+ CreateAccount(common.Address) Account
+
+ SubBalance(common.Address, *big.Int)
+ AddBalance(common.Address, *big.Int)
+ GetBalance(common.Address) *big.Int
+
+ GetNonce(common.Address) uint64
+ SetNonce(common.Address, uint64)
+
+ GetCodeHash(common.Address) common.Hash
+ GetCode(common.Address) []byte
+ SetCode(common.Address, []byte)
+ GetCodeSize(common.Address) int
+
+ AddRefund(*big.Int)
+ GetRefund() *big.Int
+
+ GetState(common.Address, common.Hash) common.Hash
+ SetState(common.Address, common.Hash, common.Hash)
+
+ Suicide(common.Address) bool
+ HasSuicided(common.Address) bool
+
+ // Exist reports whether the given account exists in state.
+ // Notably this should also return true for suicided accounts.
+ Exist(common.Address) bool
+ // Empty returns whether the given account is empty. Empty
+ // is defined according to EIP161 (balance = nonce = code = 0).
+ Empty(common.Address) bool
+
+ RevertToSnapshot(int)
+ Snapshot() int
+
+ AddLog(*Log)
+}
+
+// Account represents a contract or basic ethereum account.
+type Account interface {
+ SubBalance(amount *big.Int)
+ AddBalance(amount *big.Int)
+ SetBalance(*big.Int)
+ SetNonce(uint64)
+ Balance() *big.Int
+ Address() common.Address
+ ReturnGas(*big.Int)
+ SetCode(common.Hash, []byte)
+ ForEachStorage(cb func(key, value common.Hash) bool)
+ Value() *big.Int
+}
+
+// CallContext provides a basic interface for the EVM calling conventions. The EVM Environment
+// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
+type CallContext interface {
+ // Call another contract
+ Call(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
+ // Take another's contract code and execute within our own context
+ CallCode(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
+ // Same as CallCode except sender and value is propagated from parent to child scope
+ DelegateCall(env *Environment, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
+ // Create a new contract
+ Create(env *Environment, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
+}
diff --git a/core/vm/jit.go b/core/vm/jit.go
index b75558d39..aabe4488b 100644
--- a/core/vm/jit.go
+++ b/core/vm/jit.go
@@ -299,11 +299,11 @@ func CompileProgram(program *Program) (err error) {
// RunProgram runs the program given the environment and contract and returns an
// error if the execution failed (non-consensus)
-func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) {
+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, contract *Contract, input []byte) ([]byte, error) {
+func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env *Environment, contract *Contract, input []byte) ([]byte, error) {
contract.Input = input
var (
@@ -319,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env
}()
}
- homestead := env.ChainConfig().IsHomestead(env.BlockNumber())
+ homestead := env.ChainConfig().IsHomestead(env.BlockNumber)
for pc < uint64(len(program.instructions)) {
instrCount++
@@ -357,7 +357,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, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
+func jitCalculateGasAndSize(env *Environment, contract *Contract, instr instruction, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -408,7 +408,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
- val := statedb.GetState(contract.Address(), common.BigToHash(x))
+ val := env.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)
@@ -417,7 +417,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
g = params.SstoreSetGas
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- statedb.AddRefund(params.SstoreRefundGas)
+ env.StateDB.AddRefund(params.SstoreRefundGas)
g = params.SstoreClearGas
} else {
@@ -425,8 +425,8 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
}
gas.Set(g)
case SUICIDE:
- if !statedb.HasSuicided(contract.Address()) {
- statedb.AddRefund(params.SuicideRefundGas)
+ if !env.StateDB.HasSuicided(contract.Address()) {
+ env.StateDB.AddRefund(params.SuicideRefundGas)
}
case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32))
@@ -463,7 +463,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
gas.Add(gas, stack.data[stack.len()-1])
if op == CALL {
- if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
+ if !env.StateDB.Exist(common.BigToAddress(stack.data[stack.len()-2])) {
gas.Add(gas, params.CallNewAccountGas)
}
}
diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go
index 6f7ba9250..79c389c05 100644
--- a/core/vm/jit_test.go
+++ b/core/vm/jit_test.go
@@ -19,10 +19,8 @@ package vm
import (
"math/big"
"testing"
- "time"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
@@ -86,8 +84,8 @@ func TestCompiling(t *testing.T) {
func TestResetInput(t *testing.T) {
var sender account
- env := NewEnv(&Config{EnableJit: true, ForceJit: true})
- contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
+ env := NewEnvironment(Context{}, nil, params.TestChainConfig, Config{})
+ contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
contract.CodeAddr = &common.Address{}
program := NewProgram([]byte{})
@@ -135,7 +133,7 @@ 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) ReturnGas(*big.Int) {}
func (account) SetCode(common.Hash, []byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
@@ -145,70 +143,18 @@ func runVmBench(test vmBench, b *testing.B) {
if test.precompile && !test.forcejit {
NewProgram(test.code)
}
- env := NewEnv(&Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
+ env := NewEnvironment(Context{}, nil, params.TestChainConfig, Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
b.ResetTimer()
for i := 0; i < b.N; i++ {
- context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
+ context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
context.Code = test.code
context.CodeAddr = &common.Address{}
- _, err := env.Vm().Run(context, test.input)
+ _, err := env.EVM().Run(context, test.input)
if err != nil {
b.Error(err)
b.FailNow()
}
}
}
-
-type Env struct {
- gasLimit *big.Int
- depth int
- evm *EVM
-}
-
-func NewEnv(config *Config) *Env {
- env := &Env{gasLimit: big.NewInt(10000), depth: 0}
- env.evm = New(env, *config)
- return env
-}
-
-func (self *Env) ChainConfig() *params.ChainConfig {
- return params.TestChainConfig
-}
-func (self *Env) Vm() Vm { return self.evm }
-func (self *Env) Origin() common.Address { return common.Address{} }
-func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
-
-//func (self *Env) PrevHash() []byte { return self.parent }
-func (self *Env) Coinbase() common.Address { return common.Address{} }
-func (self *Env) SnapshotDatabase() int { return 0 }
-func (self *Env) RevertToSnapshot(int) {}
-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) 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.Keccak256([]byte(big.NewInt(int64(n)).String())))
-}
-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 common.Address, balance *big.Int) bool {
- return true
-}
-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 ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return nil, nil
-}
-func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return nil, common.Address{}, nil
-}
-func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return nil, nil
-}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 9e13d703b..6a605a59c 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -65,7 +65,7 @@ type StructLog struct {
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type Tracer interface {
- CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
+ CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
}
// StructLogger is an EVM state logger and implements Tracer.
@@ -94,7 +94,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
// captureState logs a new structured log message and pushes it out to the environment
//
// captureState also tracks SSTORE ops to track dirty values.
-func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
// check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
return TraceLimitReachedError
@@ -144,7 +144,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
storage = make(Storage)
// Get the contract account and loop over each storage entry. This may involve looping over
// the trie and is a very expensive process.
- env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
+ env.StateDB.GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
storage[key] = value
// Return true, indicating we'd like to continue.
return true
@@ -155,7 +155,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
}
}
// create a new snaptshot of the EVM.
- log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth(), err}
+ log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth, err}
l.logs = append(l.logs, log)
return nil
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index d4d164eb6..05ad32fd8 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -21,16 +21,17 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/params"
)
type dummyContractRef struct {
calledForEach bool
}
-func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
-func (dummyContractRef) Address() common.Address { return common.Address{} }
-func (dummyContractRef) Value() *big.Int { return new(big.Int) }
-func (dummyContractRef) SetCode(common.Hash, []byte) {}
+func (dummyContractRef) ReturnGas(*big.Int) {}
+func (dummyContractRef) Address() common.Address { return common.Address{} }
+func (dummyContractRef) Value() *big.Int { return new(big.Int) }
+func (dummyContractRef) SetCode(common.Hash, []byte) {}
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
d.calledForEach = true
}
@@ -40,28 +41,22 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
-type dummyEnv struct {
- *Env
+type dummyStateDB struct {
+ NoopStateDB
ref *dummyContractRef
}
-func newDummyEnv(ref *dummyContractRef) *dummyEnv {
- return &dummyEnv{
- Env: NewEnv(&Config{EnableJit: false, ForceJit: false}),
- ref: ref,
- }
-}
-func (d dummyEnv) GetAccount(common.Address) Account {
+func (d dummyStateDB) GetAccount(common.Address) Account {
return d.ref
}
func TestStoreCapture(t *testing.T) {
var (
- env = NewEnv(&Config{EnableJit: false, ForceJit: false})
+ env = NewEnvironment(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
- contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int), new(big.Int))
+ contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int))
)
stack.push(big.NewInt(1))
stack.push(big.NewInt(0))
@@ -83,8 +78,8 @@ func TestStorageCapture(t *testing.T) {
t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it")
var (
ref = &dummyContractRef{}
- contract = NewContract(ref, ref, new(big.Int), new(big.Int), new(big.Int))
- env = newDummyEnv(ref)
+ contract = NewContract(ref, ref, new(big.Int), new(big.Int))
+ env = NewEnvironment(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
diff --git a/core/vm/noop.go b/core/vm/noop.go
new file mode 100644
index 000000000..ca7d1055a
--- /dev/null
+++ b/core/vm/noop.go
@@ -0,0 +1,68 @@
+// Copyright 2016 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 (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool {
+ return true
+}
+func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {}
+
+type NoopEVMCallContext struct{}
+
+func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
+ return nil, nil
+}
+func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
+ return nil, nil
+}
+func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
+ return nil, common.Address{}, nil
+}
+func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
+ return nil, nil
+}
+
+type NoopStateDB struct{}
+
+func (NoopStateDB) GetAccount(common.Address) Account { return nil }
+func (NoopStateDB) CreateAccount(common.Address) Account { return nil }
+func (NoopStateDB) SubBalance(common.Address, *big.Int) {}
+func (NoopStateDB) AddBalance(common.Address, *big.Int) {}
+func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil }
+func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 }
+func (NoopStateDB) SetNonce(common.Address, uint64) {}
+func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} }
+func (NoopStateDB) GetCode(common.Address) []byte { return nil }
+func (NoopStateDB) SetCode(common.Address, []byte) {}
+func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
+func (NoopStateDB) AddRefund(*big.Int) {}
+func (NoopStateDB) GetRefund() *big.Int { return nil }
+func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
+func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
+func (NoopStateDB) Suicide(common.Address) bool { return false }
+func (NoopStateDB) HasSuicided(common.Address) bool { return false }
+func (NoopStateDB) Exist(common.Address) bool { return false }
+func (NoopStateDB) Empty(common.Address) bool { return false }
+func (NoopStateDB) RevertToSnapshot(int) {}
+func (NoopStateDB) Snapshot() int { return 0 }
+func (NoopStateDB) AddLog(*Log) {}
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index f1a2b60d3..3cf0dd024 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -23,92 +23,22 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/params"
)
-// Env is a basic runtime environment required for running the EVM.
-type Env struct {
- chainConfig *params.ChainConfig
- depth int
- state *state.StateDB
-
- origin common.Address
- coinbase common.Address
-
- number *big.Int
- time *big.Int
- difficulty *big.Int
- gasLimit *big.Int
-
- getHashFn func(uint64) common.Hash
-
- evm *vm.EVM
-}
-
-// NewEnv returns a new vm.Environment
-func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
- env := &Env{
- chainConfig: cfg.ChainConfig,
- state: state,
- origin: cfg.Origin,
- coinbase: cfg.Coinbase,
- number: cfg.BlockNumber,
- time: cfg.Time,
- difficulty: cfg.Difficulty,
- gasLimit: cfg.GasLimit,
+func NewEnv(cfg *Config, state *state.StateDB) *vm.Environment {
+ context := vm.Context{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ GetHash: func(uint64) common.Hash { return common.Hash{} },
+
+ Origin: cfg.Origin,
+ Coinbase: cfg.Coinbase,
+ BlockNumber: cfg.BlockNumber,
+ Time: cfg.Time,
+ Difficulty: cfg.Difficulty,
+ GasLimit: cfg.GasLimit,
+ GasPrice: new(big.Int),
}
- env.evm = vm.New(env, vm.Config{
- Debug: cfg.Debug,
- EnableJit: !cfg.DisableJit,
- ForceJit: !cfg.DisableJit,
- })
-
- return env
-}
-
-func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig }
-func (self *Env) Vm() vm.Vm { return self.evm }
-func (self *Env) Origin() common.Address { return self.origin }
-func (self *Env) BlockNumber() *big.Int { return self.number }
-func (self *Env) Coinbase() common.Address { return self.coinbase }
-func (self *Env) Time() *big.Int { return self.time }
-func (self *Env) Difficulty() *big.Int { return self.difficulty }
-func (self *Env) Db() vm.Database { return self.state }
-func (self *Env) GasLimit() *big.Int { return self.gasLimit }
-func (self *Env) VmType() vm.Type { return vm.StdVmTy }
-func (self *Env) GetHash(n uint64) common.Hash {
- return self.getHashFn(n)
-}
-func (self *Env) AddLog(log *vm.Log) {
- self.state.AddLog(log)
-}
-func (self *Env) Depth() int { return self.depth }
-func (self *Env) SetDepth(i int) { self.depth = i }
-func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
- return self.state.GetBalance(from).Cmp(balance) >= 0
-}
-func (self *Env) SnapshotDatabase() int {
- return self.state.Snapshot()
-}
-func (self *Env) RevertToSnapshot(snapshot int) {
- self.state.RevertToSnapshot(snapshot)
-}
-
-func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
- core.Transfer(from, to, amount)
-}
-
-func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.Call(self, caller, addr, data, gas, price, value)
-}
-func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
- return core.CallCode(self, caller, addr, data, gas, price, value)
-}
-
-func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
- return core.DelegateCall(self, me, addr, data, gas, price)
-}
-func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
- return core.Create(self, caller, data, gas, price, value)
+ return vm.NewEnvironment(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index d51b435f8..3e99ed689 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
@@ -49,6 +50,7 @@ type Config struct {
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
Debug bool
+ EVMConfig vm.Config
State *state.StateDB
GetHashFn func(n uint64) common.Hash
@@ -123,13 +125,37 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
receiver.Address(),
input,
cfg.GasLimit,
- cfg.GasPrice,
cfg.Value,
)
return ret, cfg.State, err
}
+// Create executes the code using the EVM create method
+func Create(input []byte, cfg *Config) ([]byte, common.Address, error) {
+ if cfg == nil {
+ cfg = new(Config)
+ }
+ setDefaults(cfg)
+
+ if cfg.State == nil {
+ db, _ := ethdb.NewMemDatabase()
+ cfg.State, _ = state.New(common.Hash{}, db)
+ }
+ var (
+ vmenv = NewEnv(cfg, cfg.State)
+ sender = cfg.State.CreateAccount(cfg.Origin)
+ )
+
+ // Call the code with the given configuration.
+ return vmenv.Create(
+ sender,
+ input,
+ cfg.GasLimit,
+ cfg.Value,
+ )
+}
+
// Call executes the code given by the contract's address. It will return the
// EVM's return value or an error if it failed.
//
@@ -147,7 +173,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
address,
input,
cfg.GasLimit,
- cfg.GasPrice,
cfg.Value,
)
diff --git a/core/vm/segments.go b/core/vm/segments.go
index 648d8a04a..47f535ab5 100644
--- a/core/vm/segments.go
+++ b/core/vm/segments.go
@@ -24,7 +24,7 @@ type jumpSeg struct {
gas *big.Int
}
-func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+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
}
@@ -42,7 +42,7 @@ type pushSeg struct {
gas *big.Int
}
-func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+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) {
diff --git a/core/vm/util_test.go b/core/vm/util_test.go
deleted file mode 100644
index 5783ee015..000000000
--- a/core/vm/util_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 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 (
- "math/big"
-
- "github.com/ethereum/go-ethereum/params"
-)
-
-type ruleSet struct {
- hs *big.Int
-}
-
-func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }
-func (r ruleSet) GasTable(*big.Int) params.GasTable {
- return params.GasTableHomestead
-}
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 56aca6912..3521839df 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -30,10 +30,17 @@ import (
// Config are the configuration options for the EVM
type Config struct {
- Debug bool
+ // Debug enabled debugging EVM options
+ Debug bool
+ // EnableJit enabled the JIT VM
EnableJit bool
- ForceJit bool
- Tracer Tracer
+ // ForceJit forces the JIT VM
+ ForceJit bool
+ // Tracer is the op code logger
+ Tracer Tracer
+ // NoRecursion disabled EVM call, callcode,
+ // delegate call and create.
+ NoRecursion bool
}
// EVM is used to run Ethereum based contracts and will utilise the
@@ -41,26 +48,26 @@ type Config struct {
// The EVM will run the byte code VM or JIT VM based on the passed
// configuration.
type EVM struct {
- env Environment
+ env *Environment
jumpTable vmJumpTable
cfg Config
gasTable params.GasTable
}
// New returns a new instance of the EVM.
-func New(env Environment, cfg Config) *EVM {
+func New(env *Environment, cfg Config) *EVM {
return &EVM{
env: env,
- jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()),
+ jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber),
cfg: cfg,
- gasTable: env.ChainConfig().GasTable(env.BlockNumber()),
+ gasTable: env.ChainConfig().GasTable(env.BlockNumber),
}
}
// Run loops and evaluates the contract's code with the given input data
func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
- evm.env.SetDepth(evm.env.Depth() + 1)
- defer evm.env.SetDepth(evm.env.Depth() - 1)
+ evm.env.Depth++
+ defer func() { evm.env.Depth-- }()
if contract.CodeAddr != nil {
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
@@ -117,10 +124,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
code = contract.Code
instrCount = 0
- op OpCode // current opcode
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
- statedb = evm.env.Db() // current state
+ op OpCode // current opcode
+ mem = NewMemory() // bound memory
+ stack = newstack() // local stack
// 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. Practically much less so feasible.
pc = uint64(0) // program counter
@@ -146,7 +152,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// 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 && evm.cfg.Debug {
- evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err)
+ evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, err)
}
}()
@@ -174,7 +180,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
op = contract.GetOp(pc)
//fmt.Printf("OP %d %v\n", op, op)
// calculate the new memory size and gas price for the current executing opcode
- newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack)
+ newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, mem, stack)
if err != nil {
return nil, err
}
@@ -189,7 +195,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
mem.Resize(newMemSize.Uint64())
// Add a log message
if evm.cfg.Debug {
- err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
+ err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, nil)
if err != nil {
return nil, err
}
@@ -242,7 +248,7 @@ func (evm *EVM) Run(contract *Contract, 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(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
+func calculateGasAndSize(gasTable params.GasTable, env *Environment, contract *Contract, caller ContractRef, op OpCode, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -260,21 +266,21 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
gas.Set(gasTable.Suicide)
var (
address = common.BigToAddress(stack.data[len(stack.data)-1])
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
+ eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
)
if eip158 {
// if empty and transfers value
- if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 {
+ if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
gas.Add(gas, gasTable.CreateBySuicide)
}
- } else if !env.Db().Exist(address) {
+ } else if !env.StateDB.Exist(address) {
gas.Add(gas, gasTable.CreateBySuicide)
}
}
- if !statedb.HasSuicided(contract.Address()) {
- statedb.AddRefund(params.SuicideRefundGas)
+ if !env.StateDB.HasSuicided(contract.Address()) {
+ env.StateDB.AddRefund(params.SuicideRefundGas)
}
case EXTCODESIZE:
gas.Set(gasTable.ExtcodeSize)
@@ -323,7 +329,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
- val := statedb.GetState(contract.Address(), common.BigToHash(x))
+ val := env.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)
@@ -333,7 +339,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
// 0 => non 0
g = params.SstoreSetGas
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
- statedb.AddRefund(params.SstoreRefundGas)
+ env.StateDB.AddRefund(params.SstoreRefundGas)
g = params.SstoreClearGas
} else {
@@ -394,13 +400,13 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
if op == CALL {
var (
address = common.BigToAddress(stack.data[len(stack.data)-2])
- eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
+ eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
)
if eip158 {
- if env.Db().Empty(address) && transfersValue {
+ if env.StateDB.Empty(address) && transfersValue {
gas.Add(gas, params.CallNewAccountGas)
}
- } else if !env.Db().Exist(address) {
+ } else if !env.StateDB.Exist(address) {
gas.Add(gas, params.CallNewAccountGas)
}
}
diff --git a/core/vm/vm_jit_fake.go b/core/vm/vm_jit_fake.go
index 4fa98ccd9..44b60abf6 100644
--- a/core/vm/vm_jit_fake.go
+++ b/core/vm/vm_jit_fake.go
@@ -17,10 +17,3 @@
// +build !evmjit
package vm
-
-import "fmt"
-
-func NewJitVm(env Environment) VirtualMachine {
- fmt.Printf("Warning! EVM JIT not enabled.\n")
- return New(env, Config{})
-}