diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/evm.go | 73 | ||||
-rw-r--r-- | core/execution.go | 217 | ||||
-rw-r--r-- | core/state/state_object.go | 2 | ||||
-rw-r--r-- | core/state/statedb.go | 9 | ||||
-rw-r--r-- | core/state_processor.go | 22 | ||||
-rw-r--r-- | core/state_transition.go | 51 | ||||
-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 | ||||
-rw-r--r-- | core/vm_env.go | 101 |
23 files changed, 783 insertions, 797 deletions
diff --git a/core/evm.go b/core/evm.go new file mode 100644 index 000000000..6a5713075 --- /dev/null +++ b/core/evm.go @@ -0,0 +1,73 @@ +// 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 core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" +) + +// BlockFetcher retrieves headers by their hash +type HeaderFetcher interface { + // GetHeader returns the hash corresponding to their hash + GetHeader(common.Hash, uint64) *types.Header +} + +// NewEVMContext creates a new context for use in the EVM. +func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context { + return vm.Context{ + CanTransfer: CanTransfer, + Transfer: Transfer, + GetHash: GetHashFn(header, chain), + + Origin: msg.From(), + Coinbase: header.Coinbase, + BlockNumber: new(big.Int).Set(header.Number), + Time: new(big.Int).Set(header.Time), + Difficulty: new(big.Int).Set(header.Difficulty), + GasLimit: new(big.Int).Set(header.GasLimit), + GasPrice: new(big.Int).Set(msg.GasPrice()), + } +} + +// GetHashFn returns a GetHashFunc which retrieves header hashes by number +func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash { + return func(n uint64) common.Hash { + for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { + if header.Number.Uint64() == n { + return header.Hash() + } + } + + return common.Hash{} + } +} + +// CanTransfer checks wether there are enough funds in the address' account to make a transfer. +// This does not take the necessary gas in to account to make the transfer valid. +func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { + return db.GetBalance(addr).Cmp(amount) >= 0 +} + +// Transfer subtracts amount from sender and adds amount to recipient using the given Db +func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { + db.SubBalance(sender, amount) + db.AddBalance(recipient, amount) +} diff --git a/core/execution.go b/core/execution.go deleted file mode 100644 index e3ea1006c..000000000 --- a/core/execution.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package core - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" -) - -// Call executes within the given contract -func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - // Depth check execution. Fail if we're trying to execute above the - // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) - - return nil, vm.DepthError - } - if !env.CanTransfer(caller.Address(), value) { - caller.ReturnGas(gas, gasPrice) - - return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) - } - - snapshotPreTransfer := env.SnapshotDatabase() - var ( - from = env.Db().GetAccount(caller.Address()) - to vm.Account - ) - if !env.Db().Exist(addr) { - if vm.Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 { - caller.ReturnGas(gas, gasPrice) - return nil, nil - } - - to = env.Db().CreateAccount(addr) - } else { - to = env.Db().GetAccount(addr) - } - env.Transfer(from, to, value) - - // initialise a new contract and set the code that is to be used by the - // EVM. The contract is a scoped environment for this execution context - // only. - contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) - defer contract.Finalise() - - ret, err = env.Vm().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.RevertToSnapshot(snapshotPreTransfer) - } - return ret, err -} - -// CallCode executes the given address' code as the given contract address -func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - // Depth check execution. Fail if we're trying to execute above the - // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) - - return nil, vm.DepthError - } - if !env.CanTransfer(caller.Address(), value) { - caller.ReturnGas(gas, gasPrice) - - return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) - } - - var ( - snapshotPreTransfer = env.SnapshotDatabase() - to = env.Db().GetAccount(caller.Address()) - ) - // initialise a new contract and set the code that is to be used by the - // EVM. The contract is a scoped environment for this execution context - // only. - contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) - defer contract.Finalise() - - ret, err = env.Vm().Run(contract, input) - if err != nil { - contract.UseGas(contract.Gas) - - env.RevertToSnapshot(snapshotPreTransfer) - } - - return ret, err -} - -// Create creates a new contract with the given code -func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) { - // Depth check execution. Fail if we're trying to execute above the - // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) - - return nil, common.Address{}, vm.DepthError - } - if !env.CanTransfer(caller.Address(), value) { - caller.ReturnGas(gas, gasPrice) - - return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) - } - - // Create a new account on the state - nonce := env.Db().GetNonce(caller.Address()) - env.Db().SetNonce(caller.Address(), nonce+1) - - snapshotPreTransfer := env.SnapshotDatabase() - var ( - addr = crypto.CreateAddress(caller.Address(), nonce) - from = env.Db().GetAccount(caller.Address()) - to = env.Db().CreateAccount(addr) - ) - if env.ChainConfig().IsEIP158(env.BlockNumber()) { - env.Db().SetNonce(addr, 1) - } - env.Transfer(from, to, value) - - // initialise a new contract and set the code that is to be used by the - // EVM. The contract is a scoped environment for this execution context - // only. - contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code) - defer contract.Finalise() - - ret, err = env.Vm().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.Db().SetCode(addr, ret) - } else { - err = vm.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 != vm.CodeStoreOutOfGasError)) { - contract.UseGas(contract.Gas) - env.RevertToSnapshot(snapshotPreTransfer) - - // Nothing should be returned when an error is thrown. - return nil, addr, err - } - - return ret, addr, err -} - -// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope -func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) { - // Depth check execution. Fail if we're trying to execute above the - // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { - caller.ReturnGas(gas, gasPrice) - return nil, vm.DepthError - } - - var ( - snapshot = env.SnapshotDatabase() - to = env.Db().GetAccount(caller.Address()) - ) - - // Iinitialise a new contract and make initialise the delegate values - contract := vm.NewContract(caller, to, caller.Value(), gas, gasPrice).AsDelegate() - contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) - defer contract.Finalise() - - ret, err = env.Vm().Run(contract, input) - if err != nil { - contract.UseGas(contract.Gas) - - env.RevertToSnapshot(snapshot) - } - - return ret, err -} - -// generic transfer method -func Transfer(from, to vm.Account, amount *big.Int) { - from.SubBalance(amount) - to.AddBalance(amount) -} diff --git a/core/state/state_object.go b/core/state/state_object.go index d40b42d83..87aa8ccd6 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -288,7 +288,7 @@ func (self *StateObject) setBalance(amount *big.Int) { } // Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *StateObject) ReturnGas(gas, price *big.Int) {} +func (c *StateObject) ReturnGas(gas *big.Int) {} func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject { stateObject := newObject(db, self.address, self.data, onDirty) diff --git a/core/state/statedb.go b/core/state/statedb.go index 3742c178b..82e2ec7c1 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -293,6 +293,7 @@ func (self *StateDB) HasSuicided(addr common.Address) bool { * SETTERS */ +// AddBalance adds amount to the account associated with addr func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { @@ -300,6 +301,14 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { } } +// SubBalance subtracts amount from the account associated with addr +func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SubBalance(amount) + } +} + func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { diff --git a/core/state_processor.go b/core/state_processor.go index e346917c3..67a7ad5a1 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -96,28 +96,36 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s if err != nil { return nil, nil, nil, err } - - _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, msg, header, cfg), msg, gp) + // Create a new context to be used in the EVM environment + context := NewEVMContext(msg, header, bc) + // Create a new environment which holds all relevant information + // about the transaction and calling mechanisms. + vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{}) + // Apply the transaction to the current state (included in the env) + _, gas, err := ApplyMessage(vmenv, msg, gp) if err != nil { return nil, nil, nil, err } // Update the state with pending changes usedGas.Add(usedGas, gas) + // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx + // based on the eip phase, we're passing wether the root touch-delete accounts. receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) - if MessageCreatesContract(msg) { - receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce()) + // if the transaction created a contract, store the creation address in the receipt. + if msg.To() == nil { + receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) } - logs := statedb.GetLogs(tx.Hash()) - receipt.Logs = logs + // Set the receipt logs and create a bloom for filtering + receipt.Logs = statedb.GetLogs(tx.Hash()) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) glog.V(logger.Debug).Infoln(receipt) - return receipt, logs, gas, err + return receipt, receipt.Logs, gas, err } // AccumulateRewards credits the coinbase of the given block with the diff --git a/core/state_transition.go b/core/state_transition.go index 8abe17b0a..48540be14 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -55,9 +55,9 @@ type StateTransition struct { initialGas *big.Int value *big.Int data []byte - state vm.Database + state vm.StateDB - env vm.Environment + env *vm.Environment } // Message represents a message sent to a contract. @@ -106,7 +106,7 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int { } // NewStateTransition initialises and returns a new state transition object. -func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTransition { +func NewStateTransition(env *vm.Environment, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ gp: gp, env: env, @@ -116,7 +116,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran initialGas: new(big.Int), value: msg.Value(), data: msg.Data(), - state: env.Db(), + state: env.StateDB, } } @@ -127,7 +127,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran // the gas used (which includes gas refunds) and an error if it failed. An error always // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. -func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { +func ApplyMessage(env *vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { st := NewStateTransition(env, msg, gp) ret, _, gasUsed, err := st.TransitionDb() @@ -217,47 +217,44 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b msg := self.msg sender := self.from() // err checked in preCheck - homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) + homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber) contractCreation := MessageCreatesContract(msg) // Pay intrinsic gas if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil { return nil, nil, nil, InvalidTxError(err) } - vmenv := self.env - //var addr common.Address + var ( + vmenv = self.env + // vm errors do not effect consensus and are therefor + // not assigned to err, except for insufficient balance + // error. + vmerr error + ) if contractCreation { - ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value) + ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value) if homestead && err == vm.CodeStoreOutOfGasError { self.gas = Big0 } - - if err != nil { - ret = nil - glog.V(logger.Core).Infoln("VM create err:", err) - } } else { // Increment the nonce for the next transaction self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1) - ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value) - if err != nil { - glog.V(logger.Core).Infoln("VM call err:", err) - } - } - - if err != nil && IsValueTransferErr(err) { - return nil, nil, nil, InvalidTxError(err) + ret, vmerr = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value) } - - // We aren't interested in errors here. Errors returned by the VM are non-consensus errors and therefor shouldn't bubble up - if err != nil { - err = nil + if vmerr != nil { + glog.V(logger.Core).Infoln("vm returned with error:", err) + // The only possible consensus-error would be if there wasn't + // sufficient balance to make the transfer happen. The first + // balance transfer may never fail. + if vmerr == vm.ErrInsufficientBalance { + return nil, nil, nil, InvalidTxError(vmerr) + } } requiredGas = new(big.Int).Set(self.gasUsed()) self.refundGas() - self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) + self.state.AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice)) return ret, requiredGas, self.gasUsed(), err } 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{}) -} diff --git a/core/vm_env.go b/core/vm_env.go index 43637bd13..58e71e305 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -15,104 +15,3 @@ // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. package core - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" -) - -// GetHashFn returns a function for which the VM env can query block hashes through -// up to the limit defined by the Yellow Paper and uses the given block chain -// to query for information. -func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash { - return func(n uint64) common.Hash { - for block := chain.GetBlockByHash(ref); block != nil; block = chain.GetBlock(block.ParentHash(), block.NumberU64()-1) { - if block.NumberU64() == n { - return block.Hash() - } - } - - return common.Hash{} - } -} - -type VMEnv struct { - chainConfig *params.ChainConfig // Chain configuration - state *state.StateDB // State to use for executing - evm *vm.EVM // The Ethereum Virtual Machine - depth int // Current execution depth - msg Message // Message appliod - - header *types.Header // Header information - chain *BlockChain // Blockchain handle - getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes -} - -func NewEnv(state *state.StateDB, chainConfig *params.ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv { - env := &VMEnv{ - chainConfig: chainConfig, - chain: chain, - state: state, - header: header, - msg: msg, - getHashFn: GetHashFn(header.ParentHash, chain), - } - - env.evm = vm.New(env, cfg) - return env -} - -func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Origin() common.Address { return self.msg.From() } -func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } -func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } -func (self *VMEnv) Time() *big.Int { return self.header.Time } -func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } -func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } -func (self *VMEnv) Value() *big.Int { return self.msg.Value() } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) Depth() int { return self.depth } -func (self *VMEnv) SetDepth(i int) { self.depth = i } -func (self *VMEnv) GetHash(n uint64) common.Hash { - return self.getHashFn(n) -} - -func (self *VMEnv) AddLog(log *vm.Log) { - self.state.AddLog(log) -} -func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool { - return self.state.GetBalance(from).Cmp(balance) >= 0 -} - -func (self *VMEnv) SnapshotDatabase() int { - return self.state.Snapshot() -} - -func (self *VMEnv) RevertToSnapshot(snapshot int) { - self.state.RevertToSnapshot(snapshot) -} - -func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { - Transfer(from, to, amount) -} - -func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return Call(self, me, addr, data, gas, price, value) -} -func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return CallCode(self, me, addr, data, gas, price, value) -} - -func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { - return DelegateCall(self, me, addr, data, gas, price) -} - -func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { - return Create(self, me, data, gas, price, value) -} |