diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-12-06 09:16:03 +0800 |
---|---|---|
committer | Felix Lange <fjl@twurst.com> | 2016-12-06 09:16:03 +0800 |
commit | 3fc7c978277051391f8ea7831559e9f4f83c3166 (patch) | |
tree | b11b41c1723e02adc098ddc9f4188a8bad782ed8 /core/vm | |
parent | 7f79d249a64ee72b185ffa9a9ed78f137b7938de (diff) | |
download | dexon-3fc7c978277051391f8ea7831559e9f4f83c3166.tar dexon-3fc7c978277051391f8ea7831559e9f4f83c3166.tar.gz dexon-3fc7c978277051391f8ea7831559e9f4f83c3166.tar.bz2 dexon-3fc7c978277051391f8ea7831559e9f4f83c3166.tar.lz dexon-3fc7c978277051391f8ea7831559e9f4f83c3166.tar.xz dexon-3fc7c978277051391f8ea7831559e9f4f83c3166.tar.zst dexon-3fc7c978277051391f8ea7831559e9f4f83c3166.zip |
core, core/vm: implemented a generic environment (#3348)
Environment is now a struct (not an interface). This
reduces a lot of tech-debt throughout the codebase where a virtual
machine environment had to be implemented in order to test or run it.
The new environment is suitable to be used en the json tests, core
consensus and light client.
Diffstat (limited to 'core/vm')
-rw-r--r-- | core/vm/contract.go | 13 | ||||
-rw-r--r-- | core/vm/environment.go | 363 | ||||
-rw-r--r-- | core/vm/errors.go | 11 | ||||
-rw-r--r-- | core/vm/instructions.go | 202 | ||||
-rw-r--r-- | core/vm/interface.go | 97 | ||||
-rw-r--r-- | core/vm/jit.go | 18 | ||||
-rw-r--r-- | core/vm/jit_test.go | 66 | ||||
-rw-r--r-- | core/vm/logger.go | 8 | ||||
-rw-r--r-- | core/vm/logger_test.go | 29 | ||||
-rw-r--r-- | core/vm/noop.go | 68 | ||||
-rw-r--r-- | core/vm/runtime/env.go | 98 | ||||
-rw-r--r-- | core/vm/runtime/runtime.go | 29 | ||||
-rw-r--r-- | core/vm/segments.go | 4 | ||||
-rw-r--r-- | core/vm/util_test.go | 32 | ||||
-rw-r--r-- | core/vm/vm.go | 60 | ||||
-rw-r--r-- | core/vm/vm_jit_fake.go | 7 |
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{}) -} |