diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 74c95f4659..573d7f95c2 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -225,7 +225,14 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM from.SetBalance(common.MaxBig) // Execute the call. msg := callmsg{call} - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) + + backend := &core.EVMBackend{ + GetHashFn: core.GetHashFn(block.ParentHash(), b.blockchain), + State: statedb, + } + context := core.ToEVMContext(chainConfig, msg, block.Header()) + vmenv := vm.NewEnvironment(context, backend, chainConfig, vm.Config{}) + gaspool := new(core.GasPool).AddGas(common.MaxBig) ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() return ret, gasUsed, err diff --git a/cmd/evm/main.go b/cmd/evm/main.go index b1a3cd9e20..84b316e387 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "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/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -47,14 +46,6 @@ var ( Name: "debug", Usage: "output full trace logs", } - ForceJitFlag = cli.BoolFlag{ - Name: "forcejit", - Usage: "forces jit compilation", - } - DisableJitFlag = cli.BoolFlag{ - Name: "nojit", - Usage: "disabled jit compilation", - } CodeFlag = cli.StringFlag{ Name: "code", Usage: "EVM code", @@ -65,19 +56,49 @@ var ( } GasFlag = cli.StringFlag{ Name: "gas", - Usage: "gas limit for the evm", + Usage: "set the gas limit for the EVM execution", Value: "10000000000", } PriceFlag = cli.StringFlag{ Name: "price", - Usage: "price set for the evm", + Usage: "set the price for the EVM execution", Value: "0", } ValueFlag = cli.StringFlag{ Name: "value", - Usage: "value set for the evm", + Usage: "set the value for the EVM execution", Value: "0", } + SenderFlag = cli.StringFlag{ + Name: "origin", + Usage: "set the origin for the EVM execution", + Value: "0x", + } + CoinbaseFlag = cli.StringFlag{ + Name: "coinbase", + Usage: "coinbase set for the evm", + Value: "0x", + } + BlockNumberFlag = cli.StringFlag{ + Name: "blocknumber", + Usage: "set the block number for the EVM execution", + Value: "1", + } + BlockTimeFlag = cli.StringFlag{ + Name: "blocktime", + Usage: "set the block time for the EVM execution", + Value: "1", + } + BlockDifficultyFlag = cli.StringFlag{ + Name: "difficulty", + Usage: "set the block difficulty for the EVM execution", + Value: "1", + } + GasLimitFlag = cli.StringFlag{ + Name: "gaslimit", + Usage: "sets the gas limit for the EVM execution", + Value: "1", + } DumpFlag = cli.BoolFlag{ Name: "dump", Usage: "dumps the state after the run", @@ -105,8 +126,6 @@ func init() { CreateFlag, DebugFlag, VerbosityFlag, - ForceJitFlag, - DisableJitFlag, SysStatFlag, CodeFlag, CodeFileFlag, @@ -115,6 +134,11 @@ func init() { ValueFlag, DumpFlag, InputFlag, + GasLimitFlag, + CoinbaseFlag, + SenderFlag, + BlockNumberFlag, + BlockTimeFlag, } app.Action = run } @@ -124,23 +148,49 @@ func run(ctx *cli.Context) error { glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) db, _ := ethdb.NewMemDatabase() - statedb, _ := state.New(common.Hash{}, db) - sender := statedb.CreateAccount(common.StringToAddress("sender")) logger := vm.NewStructLogger(nil) - vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), common.Big(ctx.GlobalString(PriceFlag.Name)), vm.Config{ - Debug: ctx.GlobalBool(DebugFlag.Name), - ForceJit: ctx.GlobalBool(ForceJitFlag.Name), - EnableJit: !ctx.GlobalBool(DisableJitFlag.Name), - Tracer: logger, - }) - tstart := time.Now() + db, err := ethdb.NewMemDatabase() + if err != nil { + panic(err) + } + st, err := state.New(common.Hash{}, db) + if err != nil { + panic(err) + } + backend := &core.EVMBackend{ + GetHashFn: func(n uint64) common.Hash { + return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) + }, + State: st, + } + context := vm.Context{ + CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer}, + Origin: common.StringToAddress(ctx.GlobalString(SenderFlag.Name)), + Coinbase: common.StringToAddress(ctx.GlobalString(CoinbaseFlag.Name)), + BlockNumber: common.String2Big(ctx.GlobalString(BlockNumberFlag.Name)), + Time: common.String2Big(ctx.GlobalString(BlockTimeFlag.Name)), + Difficulty: common.String2Big(ctx.GlobalString(BlockDifficultyFlag.Name)), + GasLimit: common.String2Big(ctx.GlobalString(GasLimitFlag.Name)), + GasPrice: common.String2Big(ctx.GlobalString(PriceFlag.Name)), + } + + vmenv := vm.NewEnvironment( + context, + backend, + params.TestChainConfig, + vm.Config{ + Debug: ctx.GlobalBool(DebugFlag.Name), + Tracer: logger, + }, + ) var ( - code []byte - ret []byte - err error + code []byte + ret []byte + tstart = time.Now() + sender = st.CreateAccount(context.Origin) ) if ctx.GlobalString(CodeFlag.Name) != "" { @@ -174,8 +224,7 @@ func run(ctx *cli.Context) error { common.Big(ctx.GlobalString(ValueFlag.Name)), ) } else { - receiver := statedb.CreateAccount(common.StringToAddress("receiver")) - + receiver := st.CreateAccount(common.StringToAddress("receiver")) receiver.SetCode(crypto.Keccak256Hash(code), code) ret, err = vmenv.Call( sender, @@ -188,8 +237,7 @@ func run(ctx *cli.Context) error { vmdone := time.Since(tstart) if ctx.GlobalBool(DumpFlag.Name) { - statedb.Commit(true) - fmt.Println(string(statedb.Dump())) + fmt.Println(string(st.Dump())) } vm.StdErrFormat(logger.StructLogs()) @@ -220,89 +268,3 @@ func main() { os.Exit(1) } } - -type VMEnv struct { - state *state.StateDB - block *types.Block - - transactor *common.Address - value *big.Int - - depth int - Gas, price *big.Int - time *big.Int - logs []vm.StructLog - - evm *vm.EVM -} - -func NewEnv(state *state.StateDB, transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv { - env := &VMEnv{ - state: state, - transactor: &transactor, - value: value, - price: price, - time: big.NewInt(time.Now().Unix()), - } - - env.evm = vm.New(env, cfg) - return env -} - -// ruleSet implements vm.ChainConfig and will always default to the homestead rule set. -type ruleSet struct{} - -func (ruleSet) IsHomestead(*big.Int) bool { return true } -func (ruleSet) GasTable(*big.Int) params.GasTable { - return params.GasTableHomesteadGasRepriceFork -} - -func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() } -func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) } -func (self *VMEnv) Origin() common.Address { return *self.transactor } -func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 } -func (self *VMEnv) Coinbase() common.Address { return *self.transactor } -func (self *VMEnv) Time() *big.Int { return self.time } -func (self *VMEnv) Difficulty() *big.Int { return common.Big1 } -func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) } -func (self *VMEnv) Value() *big.Int { return self.value } -func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } -func (self *VMEnv) GasPrice() *big.Int { return big.NewInt(1000000000) } -func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } -func (self *VMEnv) Depth() int { return 0 } -func (self *VMEnv) SetDepth(i int) { self.depth = i } -func (self *VMEnv) GetHash(n uint64) common.Hash { - if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 { - return self.block.Hash() - } - return common.Hash{} -} -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) Transfer(from, to vm.Account, amount *big.Int) { - core.Transfer(from, to, amount) -} - -func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - self.Gas = gas - return core.Call(self, caller, addr, data, gas, value) -} - -func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - return core.CallCode(self, caller, addr, data, gas, value) -} - -func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { - return core.DelegateCall(self, caller, addr, data, gas) -} - -func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { - return core.Create(self, caller, data, gas, value) -} diff --git a/common/registrar/ethreg/api.go b/common/registrar/ethreg/api.go index bbc34f6ac8..abcbbafeb6 100644 --- a/common/registrar/ethreg/api.go +++ b/common/registrar/ethreg/api.go @@ -195,7 +195,16 @@ func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr } header := be.bc.CurrentBlock().Header() - vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{}) + + backend := &core.EVMBackend{ + GetHashFn: core.GetHashFn(header.ParentHash, be.bc), + State: statedb, + } + + context := core.ToEVMContext(be.config, msg, header) + vmenv := vm.NewEnvironment(context, backend, be.config, vm.Config{}) + + //vmenv := vm.NewEnvironment(false, statedb, be.config, be.bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(common.MaxBig) res, gas, err := core.ApplyMessage(vmenv, msg, gp) diff --git a/core/evm.go b/core/evm.go new file mode 100644 index 0000000000..b19e7545bb --- /dev/null +++ b/core/evm.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 . + +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" +) + +// ToEVMContext creates a new context for use in the EVM. +func ToEVMContext(config *params.ChainConfig, msg Message, header *types.Header) vm.Context { + var from common.Address + if config.IsHomestead(header.Number) { + from, _ = msg.From() + } else { + from, _ = msg.FromFrontier() + } + + return vm.Context{ + CallContext: EVMCallContext{CanTransfer, Transfer}, + Origin: 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 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{} + } +} + +// CanTransfer checks wether there are enough funds in the address' account to make a transfer. +func CanTransfer(db vm.Database, 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.Database, sender, recipient common.Address, amount *big.Int) { + db.SubBalance(sender, amount) + db.AddBalance(recipient, amount) +} + +// EVMBackend implements vm.Backend and provides an interface to keep track of the current +// state. +type EVMBackend struct { + GetHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes + State *state.StateDB +} + +// MakeSnapshot returns a copy of the state. +func (b *EVMBackend) SnapshotDatabase() int { + return b.State.Snapshot() +} + +// Get returns the state +func (b *EVMBackend) Get() vm.Database { return b.State } + +// Set sets the current state +func (b *EVMBackend) RevertToSnapshot(revision int) { b.State.RevertToSnapshot(revision) } + +// GetHash returns the canonical hash referenced by the depth +func (b *EVMBackend) GetHash(n uint64) common.Hash { + return b.GetHashFn(n) +} diff --git a/core/execution.go b/core/execution.go index 00a856d22c..05eea24044 100644 --- a/core/execution.go +++ b/core/execution.go @@ -25,31 +25,36 @@ import ( "github.com/ethereum/go-ethereum/params" ) +type EVMCallContext struct { + CanTransfer func(db vm.Database, addr common.Address, amount *big.Int) bool + Transfer func(db vm.Database, sender, recipient common.Address, amount *big.Int) +} + // Call executes within the given contract -func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { - ret, _, err = exec(true, env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value) +func (c EVMCallContext) Call(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { + ret, _, err = c.exec(true, env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value) 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, value *big.Int) (ret []byte, err error) { +func (c EVMCallContext) CallCode(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) { callerAddr := caller.Address() - ret, _, err = exec(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value) + ret, _, err = c.exec(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, value) return ret, 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 *big.Int) (ret []byte, err error) { +func (c EVMCallContext) DelegateCall(env *vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) { callerAddr := caller.Address() - originAddr := env.Origin() + originAddr := env.Origin callerValue := caller.Value() - ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, callerValue) + ret, _, err = c.execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, callerValue) return ret, err } // Create creates a new contract with the given code -func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, value *big.Int) (ret []byte, address common.Address, err error) { - ret, address, err = exec(true, env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, value) +func (c EVMCallContext) Create(env *vm.Environment, caller vm.ContractRef, code []byte, gas, value *big.Int) (ret []byte, address common.Address, err error) { + ret, address, err = c.exec(true, env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, value) // Here we get an error if we run into maximum stack depth, // See: https://github.com/ethereum/yellowpaper/pull/131 // and YP definitions for CREATE instruction @@ -59,17 +64,17 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, value * return ret, address, err } -func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) { +func (c EVMCallContext) exec(transfers bool, env *vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) { evm := env.Vm() // Depth check execution. Fail if we're trying to execute above the // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { + if env.Depth > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas.Uint64()) return nil, common.Address{}, vm.DepthError } - if !env.CanTransfer(caller.Address(), value) { + if !c.CanTransfer(env.Db(), caller.Address(), value) { caller.ReturnGas(gas.Uint64()) return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) @@ -85,14 +90,14 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co createAccount = true } - snapshotPreTransfer := env.SnapshotDatabase() + snapshotPreTransfer := env.Backend.SnapshotDatabase() var ( from = env.Db().GetAccount(caller.Address()) to vm.Account ) if createAccount { to = env.Db().CreateAccount(*address) - if env.ChainConfig().IsEIP158(env.BlockNumber()) { + if env.ChainConfig().IsEIP158(env.BlockNumber) { env.Db().SetNonce(*address, 1) } } else { @@ -103,7 +108,7 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co } } if transfers { - env.Transfer(from, to, value) + c.Transfer(env.Db(), from.Address(), to.Address(), value) } // initialise a new contract and set the code that is to be used by the @@ -131,25 +136,25 @@ func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, co // 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 && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { + if err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != vm.CodeStoreOutOfGasError) { contract.UseGas(contract.Gas()) - env.RevertToSnapshot(snapshotPreTransfer) + env.Backend.RevertToSnapshot(snapshotPreTransfer) } return ret, addr, err } -func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) { +func (c EVMCallContext) execDelegateCall(env *vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) { evm := env.Vm() // Depth check execution. Fail if we're trying to execute above the // limit. - if env.Depth() > int(params.CallCreateDepth.Int64()) { + if env.Depth > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas.Uint64()) return nil, common.Address{}, vm.DepthError } - snapshot := env.SnapshotDatabase() + snapshot := env.Backend.SnapshotDatabase() var to vm.Account if !env.Db().Exist(*toAddr) { @@ -167,14 +172,8 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA if err != nil { contract.UseGas(contract.Gas()) - env.RevertToSnapshot(snapshot) + env.Backend.RevertToSnapshot(snapshot) } return ret, addr, err } - -// generic transfer method -func Transfer(from, to vm.Account, amount *big.Int) { - from.SubBalance(amount) - to.AddBalance(amount) -} diff --git a/core/state/dump.go b/core/state/dump.go index 8294d61b9f..aa79f96726 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -67,6 +67,24 @@ func (self *StateDB) RawDump() Dump { } dump.Accounts[common.Bytes2Hex(addr)] = account } + for addr, stateObject := range self.stateObjects { + account := DumpAccount{ + Balance: stateObject.Balance().String(), + Nonce: stateObject.Nonce(), + Root: common.Bytes2Hex(stateObject.data.Root[:]), + CodeHash: common.Bytes2Hex(stateObject.CodeHash()), + Code: common.Bytes2Hex(stateObject.Code(self.db)), + Storage: make(map[string]string), + } + storageIt := stateObject.getTrie(self.db).Iterator() + for storageIt.Next() { + account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) + } + for storageAddr, value := range stateObject.cachedStorage { + account.Storage[common.Bytes2Hex(storageAddr[:])] = value.Hex() + } + dump.Accounts[common.Bytes2Hex(addr[:])] = account + } return dump } diff --git a/core/state/statedb.go b/core/state/statedb.go index c6abfdc801..2fd5518237 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -302,6 +302,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { } } +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 9d78400627..ba7590f6e5 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -91,7 +91,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // ApplyTransactions returns the generated receipts and vm logs during the // execution of the state transition phase. func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) { - _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp) + backend := &EVMBackend{ + GetHashFn: GetHashFn(header.ParentHash, bc), + State: statedb, + } + context := ToEVMContext(config, tx, header) + + env := vm.NewEnvironment(context, backend, config, cfg) + + _, gas, err := ApplyMessage(env, tx, gp) if err != nil { return nil, nil, nil, err } @@ -106,13 +114,12 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) } - logs := statedb.GetLogs(tx.Hash()) - receipt.Logs = logs + 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 0acc290475..5c0765e982 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -57,7 +57,7 @@ type StateTransition struct { data []byte state vm.Database - 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, @@ -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() @@ -139,7 +139,7 @@ func (self *StateTransition) from() (vm.Account, error) { f common.Address err error ) - if self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) { + if self.env.ChainConfig().IsHomestead(self.env.BlockNumber) { f, err = self.msg.From() } else { f, err = self.msg.FromFrontier() @@ -234,7 +234,7 @@ 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 { @@ -274,7 +274,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b requiredGas = new(big.Int).Set(self.gasUsed()) self.refundGas() - self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) + self.env.Db().AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice)) return ret, requiredGas, self.gasUsed(), err } diff --git a/core/vm/environment.go b/core/vm/environment.go deleted file mode 100644 index bed3caa29c..0000000000 --- a/core/vm/environment.go +++ /dev/null @@ -1,126 +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 . - -package vm - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "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 - // The gas price of the current call - GasPrice() *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, 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, 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 *big.Int) ([]byte, error) - // Create a new contract - Create(me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) -} - -// 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) -} - -// Database is a EVM database for full state querying. -type Database interface { - GetAccount(common.Address) Account - CreateAccount(common.Address) Account - - AddBalance(common.Address, *big.Int) - GetBalance(common.Address) *big.Int - - GetNonce(common.Address) uint64 - SetNonce(common.Address, uint64) - - GetCodeHash(common.Address) common.Hash - GetCodeSize(common.Address) int - GetCode(common.Address) []byte - SetCode(common.Address, []byte) - - AddRefund(*big.Int) - GetRefund() *big.Int - - GetState(common.Address, common.Hash) common.Hash - SetState(common.Address, common.Hash, common.Hash) - - 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(common.Address) bool -} - -// 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(uint64) - SetCode(common.Hash, []byte) - ForEachStorage(cb func(key, value common.Hash) bool) - Value() *big.Int -} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 70b32e8e52..59fc4e02e3 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -27,14 +27,14 @@ import ( type programInstruction interface { // executes the program instruction and allows the instruction to modify the state of the program - do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) + do(vm *EVM, 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 @@ -58,12 +58,13 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract return mapping[to.Uint64()], nil } -func (instr instruction) do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (instr instruction) do(vm *EVM, 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 := calculateGasAndSize(vm.gasTable, env, contract, instr, env.Db(), memory, stack) if err != nil { return nil, err } + //fmt.Printf("%04d: %-8v %v\n", *pc, instr.op, cost) // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error @@ -114,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))) @@ -142,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)) @@ -162,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)) @@ -171,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 { @@ -191,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) { x, y := stack.pop(), stack.pop() stack.push(U256(x.Exp(x, y, Pow256))) } -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) @@ -213,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)) @@ -227,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)) @@ -236,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)) @@ -245,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)) @@ -254,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)) @@ -263,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)) @@ -272,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()])) @@ -293,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) @@ -303,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) @@ -314,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.GetPtr(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) 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() @@ -361,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))) 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() @@ -383,7 +384,7 @@ 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() @@ -395,58 +396,58 @@ func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Con 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(env.GasPrice())) +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() @@ -455,59 +456,59 @@ 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.Db().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.GetPtr(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() 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)) } -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).SetUint64(contract.gas64)) } -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() @@ -515,7 +516,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract //gas = new(big.Int).SetUint64(contract.gas64) gas = contract.gas64 ) - if env.ChainConfig().IsEIP150(env.BlockNumber()) { + if env.ChainConfig().IsEIP150(env.BlockNumber) { gas = gas - gas/64 } @@ -525,7 +526,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract // 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)) @@ -534,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() @@ -565,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() @@ -596,7 +597,7 @@ 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) @@ -610,12 +611,12 @@ 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) { +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) @@ -626,7 +627,7 @@ func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contrac // 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++ { @@ -634,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.Db().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 @@ -650,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)) } } @@ -659,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 0000000000..e2fda32e4c --- /dev/null +++ b/core/vm/interface.go @@ -0,0 +1,203 @@ +// 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 . + +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" +) + +// 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) +} + +// RuleSet is an interface that defines the current rule set during the +// execution of the EVM instructions (e.g. whether it's homestead) +type RuleSet interface { + IsHomestead(*big.Int) bool +} + +// Database is a EVM database for full state querying. +type Database 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(common.Address) bool + + 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(uint64) + SetCode(common.Hash, []byte) + ForEachStorage(cb func(key, value common.Hash) bool) + Value() *big.Int +} + +// Context provides the EVM with auxilary information. Once provided it shouldn't be modified. +type Context struct { + CallContext + + // Message information + Origin common.Address // Provides information for ORIGIN + Coinbase common.Address // Provides information for COINBASE + GasPrice *big.Int // Provides information for GASPRICE + + // Block information + 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 +} + +// 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) +} + +// Backend is the basic interface for keeping track of state and taking care of +// returning ancestory data. +type Backend interface { + // GetHash returns the hash corresponding to n + GetHash(n uint64) common.Hash + // Creates a restorable snapshot + SnapshotDatabase() int + // Set database to previous snapshot + RevertToSnapshot(int) + // Get returns the database + Get() Database +} + +type Environment struct { + Context + + Backend Backend + + chainConfig *params.ChainConfig + vmConfig Config + + evm Vm + + Depth int +} + +func NewEnvironment(context Context, backend Backend, chainConfig *params.ChainConfig, vmCfg Config) *Environment { + env := &Environment{ + Context: context, + Backend: backend, + vmConfig: vmCfg, + chainConfig: chainConfig, + } + env.evm = New(env, vmCfg) + return env +} + +func (env *Environment) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + if env.vmConfig.Test && env.Depth > 0 { + caller.ReturnGas(gas.Uint64()) + + return nil, nil + } + + return env.CallContext.Call(env, caller, addr, data, gas, value) +} + +// Take another's contract code and execute within our own context +func (env *Environment) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { + if env.vmConfig.Test && env.Depth > 0 { + caller.ReturnGas(gas.Uint64()) + + return nil, nil + } + + return env.CallContext.CallCode(env, caller, addr, data, gas, value) +} + +// Same as CallCode except sender and value is propagated from parent to child scope +func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { + if env.vmConfig.Test && env.Depth > 0 { + caller.ReturnGas(gas.Uint64()) + + return nil, nil + } + + return env.CallContext.DelegateCall(env, caller, addr, data, gas) +} + +// Create a new contract +func (env *Environment) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { + if env.vmConfig.Test && env.Depth > 0 { + caller.ReturnGas(gas.Uint64()) + + return nil, common.Address{}, nil + } + + return env.CallContext.Create(env, caller, data, gas, value) +} + +// The current ruleset +func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig } +func (env *Environment) Vm() Vm { return env.evm } +func (env *Environment) Db() Database { return env.Backend.Get() } +func (env *Environment) GetHash(n uint64) common.Hash { + return env.Backend.GetHash(n) +} diff --git a/core/vm/logger.go b/core/vm/logger.go index 9e13d703be..744372c9e7 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 @@ -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 a7aa0cfa0e..1009799f92 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -21,8 +21,13 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" ) +type ruleset struct{} + +func (ruleset) IsHomestead(*big.Int) bool { return true } + type dummyContractRef struct { calledForEach bool } @@ -41,14 +46,14 @@ func (d *dummyContractRef) SetNonce(uint64) {} func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } type dummyEnv struct { - *Env + *Environment ref *dummyContractRef } func newDummyEnv(ref *dummyContractRef) *dummyEnv { return &dummyEnv{ - Env: NewEnv(&Config{EnableJit: false, ForceJit: false}), - ref: ref, + Environment: NewEnvironment(Context{}, nil, params.TestChainConfig, Config{}), + ref: ref, } } func (d dummyEnv) GetAccount(common.Address) Account { @@ -57,7 +62,7 @@ func (d dummyEnv) GetAccount(common.Address) Account { func TestStoreCapture(t *testing.T) { var ( - env = NewEnv(&Config{EnableJit: false, ForceJit: false}) + env = NewEnvironment(Context{}, nil, params.TestChainConfig, Config{}) logger = NewStructLogger(nil) mem = NewMemory() stack = newstack() @@ -90,13 +95,13 @@ func TestStorageCapture(t *testing.T) { stack = newstack() ) - logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) + logger.CaptureState(env.Environment, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) if ref.calledForEach { t.Error("didn't expect for each to be called") } logger = NewStructLogger(&LogConfig{FullStorage: true}) - logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) + logger.CaptureState(env.Environment, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) if !ref.calledForEach { t.Error("expected for each to be called") } diff --git a/core/vm/program.go b/core/vm/program.go index ebfe314db6..66e2dcfbb3 100644 --- a/core/vm/program.go +++ b/core/vm/program.go @@ -288,11 +288,9 @@ func CompileProgram(program *Program) { program.addInstr(op, pc, nil, nil) } } - - optimiseProgram(program) } -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 New(env, Config{}).Run(contract, input) } @@ -310,7 +308,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool { // 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, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) { +func calculateGasAndSize(gasTable params.GasTable, env *Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) { var ( newMemSize, memGas uint64 sizeFault bool @@ -329,7 +327,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co gas += gasTable.Suicide var ( address = common.BigToAddress(stack.data[len(stack.data)-1]) - eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber) ) switch { @@ -558,7 +556,7 @@ 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) ) switch { diff --git a/core/vm/program_optimiser.go b/core/vm/program_optimiser.go index 6d1ac93264..79c91df3e6 100644 --- a/core/vm/program_optimiser.go +++ b/core/vm/program_optimiser.go @@ -26,7 +26,7 @@ import ( // optimeProgram optimises a JIT program creating segments out of program // instructions. Currently covered are multi-pushes and static jumps -func optimiseProgram(program *Program) { +func OptimiseProgram(program *Program) { var load []instruction var ( diff --git a/core/vm/program_test.go b/core/vm/program_test.go index 337cd363f4..e4694d8ae3 100644 --- a/core/vm/program_test.go +++ b/core/vm/program_test.go @@ -16,21 +16,14 @@ 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" -) +import "testing" const maxRun = 1000 func TestSegmenting(t *testing.T) { prog := NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, 0x0}) CompileProgram(prog) + OptimiseProgram(prog) if instr, ok := prog.instructions[0].(pushSeg); ok { if len(instr.data) != 2 { @@ -42,6 +35,7 @@ func TestSegmenting(t *testing.T) { prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)}) CompileProgram(prog) + OptimiseProgram(prog) if _, ok := prog.instructions[1].(jumpSeg); ok { } else { @@ -50,6 +44,7 @@ func TestSegmenting(t *testing.T) { prog = NewProgram([]byte{byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(PUSH1), 0x1, byte(JUMP)}) CompileProgram(prog) + OptimiseProgram(prog) if instr, ok := prog.instructions[0].(pushSeg); ok { if len(instr.data) != 2 { @@ -73,8 +68,9 @@ func TestCompiling(t *testing.T) { } } +/* func TestResetInput(t *testing.T) { - var sender account + var sender Account env := NewEnv(&Config{}) contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000)) @@ -86,6 +82,7 @@ func TestResetInput(t *testing.T) { t.Errorf("expected input to be nil, got %x", contract.Input) } } +*/ func TestPcMappingToInstruction(t *testing.T) { program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)}) @@ -94,112 +91,3 @@ func TestPcMappingToInstruction(t *testing.T) { t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4]) } } - -var benchmarks = map[string]vmBench{ - "pushes": vmBench{ - false, false, false, - common.Hex2Bytes("600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01600a600a01"), nil, - }, -} - -func BenchmarkPushes(b *testing.B) { - runVmBench(benchmarks["pushes"], b) -} - -type vmBench struct { - precompile bool // compile prior to executing - nojit bool // ignore jit (sets DisbaleJit = true - forcejit bool // forces the jit, precompile is ignored - - code []byte - input []byte -} - -type account struct{} - -func (account) SubBalance(amount *big.Int) {} -func (account) AddBalance(amount *big.Int) {} -func (account) SetAddress(common.Address) {} -func (account) Value() *big.Int { return nil } -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(uint64) {} -func (account) SetCode(common.Hash, []byte) {} -func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} - -func runVmBench(test vmBench, b *testing.B) { - var sender account - - if test.precompile && !test.forcejit { - NewProgram(test.code) - } - env := NewEnv(&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)) - context.Code = test.code - context.CodeAddr = &common.Address{} - _, err := env.Vm().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 ¶ms.ChainConfig{new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int)} -} -func (self *Env) Vm() Vm { return self.evm } -func (self *Env) GasPrice() *big.Int { return new(big.Int) } -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, value *big.Int) ([]byte, error) { - return nil, nil -} -func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - return nil, nil -} -func (self *Env) Create(caller ContractRef, data []byte, gas, price *big.Int) ([]byte, common.Address, error) { - return nil, common.Address{}, nil -} -func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { - return nil, nil -} diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go deleted file mode 100644 index 7a0531edbd..0000000000 --- a/core/vm/runtime/env.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2015 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 . - -package runtime - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "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 - gasPrice *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, - gasPrice: cfg.GasPrice, - } - 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) RuleSet() vm.RuleSet { return self.ruleSet } -func (self *Env) GasPrice() *big.Int { return self.gasPrice } -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, value *big.Int) ([]byte, error) { - return core.Call(self, caller, addr, data, gas, value) -} -func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - return core.CallCode(self, caller, addr, data, gas, value) -} - -func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { - return core.DelegateCall(self, me, addr, data, gas) -} - -func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { - return core.Create(self, caller, data, gas, value) -} diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 6674d12088..0a816ea377 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -17,11 +17,14 @@ package runtime import ( + "math" "math/big" "time" "github.com/ethereum/go-ethereum/common" + "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/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" @@ -44,7 +47,7 @@ type Config struct { Coinbase common.Address BlockNumber *big.Int Time *big.Int - GasLimit *big.Int + GasLimit uint64 GasPrice *big.Int Value *big.Int DisableJit bool // "disable" so it's enabled by default @@ -66,8 +69,8 @@ func setDefaults(cfg *Config) { if cfg.Time == nil { cfg.Time = big.NewInt(time.Now().Unix()) } - if cfg.GasLimit == nil { - cfg.GasLimit = new(big.Int).Set(common.MaxBig) + if cfg.GasLimit == 0 { + cfg.GasLimit = math.MaxUint64 } if cfg.GasPrice == nil { cfg.GasPrice = new(big.Int) @@ -101,8 +104,25 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { db, _ := ethdb.NewMemDatabase() cfg.State, _ = state.New(common.Hash{}, db) } + + backend := &core.EVMBackend{ + GetHashFn: cfg.GetHashFn, + State: cfg.State, + } + + context := vm.Context{ + CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer}, + Origin: cfg.Origin, + Coinbase: cfg.Coinbase, + BlockNumber: cfg.BlockNumber, + Time: cfg.Time, + Difficulty: cfg.Difficulty, + GasLimit: new(big.Int).SetUint64(cfg.GasLimit), + GasPrice: cfg.GasPrice, + } + vmenv := vm.NewEnvironment(context, backend, params.TestChainConfig, vm.Config{}) + var ( - vmenv = NewEnv(cfg, cfg.State) sender = cfg.State.CreateAccount(cfg.Origin) receiver = cfg.State.CreateAccount(common.StringToAddress("contract")) ) @@ -114,7 +134,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { sender, receiver.Address(), input, - cfg.GasLimit, + new(big.Int).SetUint64(cfg.GasLimit), cfg.Value, ) @@ -129,7 +149,22 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) { setDefaults(cfg) - vmenv := NewEnv(cfg, cfg.State) + backend := &core.EVMBackend{ + GetHashFn: cfg.GetHashFn, + State: cfg.State, + } + + context := vm.Context{ + CallContext: core.EVMCallContext{core.CanTransfer, core.Transfer}, + Origin: cfg.Origin, + Coinbase: cfg.Coinbase, + BlockNumber: cfg.BlockNumber, + Time: cfg.Time, + Difficulty: cfg.Difficulty, + GasLimit: new(big.Int).SetUint64(cfg.GasLimit), + GasPrice: cfg.GasPrice, + } + vmenv := vm.NewEnvironment(context, backend, params.TestChainConfig, vm.Config{}) sender := cfg.State.GetOrNewStateObject(cfg.Origin) // Call the code with the given configuration. @@ -137,7 +172,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) { sender, address, input, - cfg.GasLimit, + new(big.Int).SetUint64(cfg.GasLimit), cfg.Value, ) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 88c76c7319..725dda9a0d 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -39,8 +39,8 @@ func TestDefaults(t *testing.T) { if cfg.Time == nil { t.Error("expected time to be non nil") } - if cfg.GasLimit == nil { - t.Error("expected time to be non nil") + if cfg.GasLimit == 0 { + t.Error("expected time to be non 0") } if cfg.GasPrice == nil { t.Error("expected time to be non nil") diff --git a/core/vm/segments.go b/core/vm/segments.go index cac245d243..76e662ea07 100644 --- a/core/vm/segments.go +++ b/core/vm/segments.go @@ -24,7 +24,7 @@ type jumpSeg struct { gas uint64 } -func (j jumpSeg) do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (j jumpSeg) do(vm *EVM, 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 uint64 } -func (s pushSeg) do(vm *EVM, program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (s pushSeg) do(vm *EVM, 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/vm.go b/core/vm/vm.go index 8d5c021348..dbd10c0d5e 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -29,10 +29,12 @@ import ( // Config are the configuration options for the EVM type Config struct { - Debug bool - EnableJit bool - ForceJit bool - Tracer Tracer + Test bool // boolean to indicate this is a VM Test + + ForceCompile bool + NoOptimise bool // skip optimising the program if it hasn't been compiled + Debug bool + Tracer Tracer } // EVM is used to run Ethereum based contracts and will utilise the @@ -40,26 +42,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) ([]byte, 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, exist := PrecompiledContracts[*contract.CodeAddr]; exist { @@ -87,6 +89,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) { // Create and compile program program := NewProgram(contract.Code) CompileProgram(program) + if !evm.cfg.NoOptimise { + OptimiseProgram(program) + } return evm.runProgram(program, contract, input) case progCompile: @@ -118,7 +123,7 @@ func (evm *EVM) runProgram(program *Program, contract *Contract, input []byte) ( }() } - homestead := env.ChainConfig().IsHomestead(env.BlockNumber()) + homestead := env.ChainConfig().IsHomestead(env.BlockNumber) for pc < uint64(len(program.instructions)) { instrCount++ diff --git a/core/vm/vm_jit_fake.go b/core/vm/vm_jit_fake.go index 4fa98ccd9c..4d6acf37fc 100644 --- a/core/vm/vm_jit_fake.go +++ b/core/vm/vm_jit_fake.go @@ -20,7 +20,7 @@ package vm import "fmt" -func NewJitVm(env Environment) VirtualMachine { +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 deleted file mode 100644 index 4bd42c0a20..0000000000 --- a/core/vm_env.go +++ /dev/null @@ -1,119 +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 . - -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 { f, _ := self.msg.From(); return f } -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) GasPrice() *big.Int { return self.msg.GasPrice() } -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, value *big.Int) ([]byte, error) { - return Call(self, me, addr, data, gas, value) -} -func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - return CallCode(self, me, addr, data, gas, value) -} - -func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { - return DelegateCall(self, me, addr, data, gas) -} - -func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { - return Create(self, me, data, gas, value) -} diff --git a/eth/api.go b/eth/api.go index 7932bbcb6d..be6d460a9b 100644 --- a/eth/api.go +++ b/eth/api.go @@ -521,9 +521,17 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. value: tx.Value(), data: tx.Data(), } + + backend := &core.EVMBackend{ + GetHashFn: core.GetHashFn(block.ParentHash(), api.eth.BlockChain()), + State: stateDb, + } + context := core.ToEVMContext(api.config, msg, block.Header()) + // Mutate the state if we haven't reached the tracing transaction yet if uint64(idx) < txIndex { - vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{}) + vmenv := vm.NewEnvironment(context, backend, api.config, vm.Config{}) + //vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{}) _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return nil, fmt.Errorf("mutation failed: %v", err) @@ -532,7 +540,8 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. continue } // Otherwise trace the transaction and return - vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer}) + vmenv := vm.NewEnvironment(context, backend, api.config, vm.Config{Debug: true, Tracer: tracer}) + //vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer}) ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) diff --git a/eth/api_backend.go b/eth/api_backend.go index 9d8ed1c517..bcea7d7209 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -97,13 +97,20 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { return b.eth.blockchain.GetTdByHash(blockHash) } -func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { +func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.Environment, func() error, error) { statedb := state.(EthApiState).state addr, _ := msg.From() from := statedb.GetOrNewStateObject(addr) from.SetBalance(common.MaxBig) vmError := func() error { return nil } - return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil + + backend := &core.EVMBackend{ + GetHashFn: core.GetHashFn(header.ParentHash, b.eth.blockchain), + State: statedb, + } + context := core.ToEVMContext(b.eth.chainConfig, msg, header) + + return vm.NewEnvironment(context, backend, b.eth.chainConfig, vm.Config{}), vmError, nil } func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 0aa3da18d0..3b8ae77e54 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -50,7 +50,7 @@ type Backend interface { GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) GetTd(blockHash common.Hash) *big.Int - GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (vm.Environment, func() error, error) + GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.Environment, func() error, error) // TxPool API SendTx(ctx context.Context, signedTx *types.Transaction) error RemoveTx(txHash common.Hash) diff --git a/internal/ethapi/tracer.go b/internal/ethapi/tracer.go index 5f69826a3b..f4dbe404d8 100644 --- a/internal/ethapi/tracer.go +++ b/internal/ethapi/tracer.go @@ -278,7 +278,7 @@ func wrapError(context string, err error) error { } // CaptureState implements the Tracer interface to trace a single step of VM execution -func (jst *JavascriptTracer) CaptureState(env vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { +func (jst *JavascriptTracer) CaptureState(env *vm.Environment, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { if jst.err == nil { jst.memory.memory = memory jst.stack.stack = stack diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go index b88178a6d7..8ee4ae8f74 100644 --- a/internal/ethapi/tracer_test.go +++ b/internal/ethapi/tracer_test.go @@ -24,8 +24,11 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "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/crypto" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -37,7 +40,7 @@ type Env struct { func NewEnv(config *vm.Config) *Env { env := &Env{gasLimit: big.NewInt(10000), depth: 0} - env.evm = vm.New(env, *config) + //env.evm = vm.New(env, *config) return env } @@ -91,14 +94,20 @@ 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(uint64) {} func (account) SetCode(common.Hash, []byte) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} func runTrace(tracer *JavascriptTracer) (interface{}, error) { - env := NewEnv(&vm.Config{Debug: true, Tracer: tracer}) + db, _ := ethdb.NewMemDatabase() + st, _ := state.New(common.Hash{}, db) + backend := &core.EVMBackend{ + GetHashFn: nil, + State: st, + } + env := vm.NewEnvironment(vm.Context{GasLimit: big.NewInt(1000000)}, backend, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) - contract := vm.NewContract(account{}, account{}, big.NewInt(0), env.GasLimit(), big.NewInt(1)) + contract := vm.NewContract(account{}, account{}, big.NewInt(0), env.GasLimit) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} _, err := env.Vm().Run(contract, []byte{}) @@ -175,7 +184,7 @@ func TestHalt(t *testing.T) { tracer.Stop(timeout) }() - if _, err = runTrace(tracer); err.Error() != "stahp in server-side tracer function 'step'" { + if _, err = runTrace(tracer); err == nil || (err != nil && err.Error() != "stahp in server-side tracer function 'step'") { t.Errorf("Expected timeout error, got %v", err) } } @@ -186,8 +195,14 @@ func TestHaltBetweenSteps(t *testing.T) { t.Fatal(err) } - env := NewEnv(&vm.Config{Debug: true, Tracer: tracer}) - contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0), big.NewInt(0)) + db, _ := ethdb.NewMemDatabase() + st, _ := state.New(common.Hash{}, db) + backend := &core.EVMBackend{ + GetHashFn: nil, + State: st, + } + env := vm.NewEnvironment(vm.Context{GasLimit: big.NewInt(1000000)}, backend, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) + contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0)) tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil) timeout := errors.New("stahp") diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 6efa7024ee..4ec7cf158f 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -105,6 +105,8 @@ func benchStateTest(chainConfig *params.ChainConfig, test VmTest, env map[string } func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, skipTests []string) error { + //glog.SetToStderr(true) + //glog.SetV(6) skipTest := make(map[string]bool, len(skipTests)) for _, name := range skipTests { skipTest[name] = true @@ -225,25 +227,23 @@ func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx m to = &t } // Set pre compiled contracts - snapshot := statedb.Snapshot() gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"])) key, _ := hex.DecodeString(tx["secretKey"]) addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) message := NewMessage(addr, to, data, value, gas, price, nonce) - vmenv := NewEnvFromMap(chainConfig, statedb, env, tx) - vmenv.origin = addr - root, _ := statedb.Commit(false) statedb.Reset(root) snapshot := statedb.Snapshot() + vmenv := NewEVMEnvironment(false, chainConfig, statedb, env, tx) + ret, _, err := core.ApplyMessage(vmenv, message, gaspool) if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { statedb.RevertToSnapshot(snapshot) } - statedb.Commit(chainConfig.IsEIP158(vmenv.BlockNumber())) + statedb.Commit(chainConfig.IsEIP158(vmenv.BlockNumber)) - return ret, vmenv.state.Logs(), vmenv.Gas, err + return ret, statedb.Logs(), gas, err } diff --git a/tests/util.go b/tests/util.go index f4b229c017..c9d67abdd4 100644 --- a/tests/util.go +++ b/tests/util.go @@ -18,6 +18,7 @@ package tests import ( "bytes" + "encoding/hex" "fmt" "math/big" "os" @@ -148,141 +149,60 @@ type VmTest struct { PostStateRoot string } -type Env struct { - chainConfig *params.ChainConfig - depth int - state *state.StateDB - skipTransfer bool - initial bool - Gas, gasPrice *big.Int - - origin common.Address - parent common.Hash - coinbase common.Address - - number *big.Int - time *big.Int - difficulty *big.Int - gasLimit *big.Int - - vmTest bool - - evm vm.VirtualMachine +type RuleSet struct { + HomesteadBlock *big.Int + DAOForkBlock *big.Int + DAOForkSupport bool } -func NewEnv(chainConfig *params.ChainConfig, state *state.StateDB) *Env { - env := &Env{ - chainConfig: chainConfig, - state: state, +func (r RuleSet) IsHomestead(n *big.Int) bool { + return n.Cmp(r.HomesteadBlock) >= 0 +} + +func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *vm.Environment { + origin := common.HexToAddress(exeValues["caller"]) + if len(exeValues["secretKey"]) > 0 { + key, _ := hex.DecodeString(exeValues["secretKey"]) + origin = crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) } - return env -} -func NewEnvFromMap(chainConfig *params.ChainConfig, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env { - env := NewEnv(chainConfig, state) + context := vm.Context{ + Origin: origin, + Coinbase: common.HexToAddress(envValues["currentCoinbase"]), + BlockNumber: common.Big(envValues["currentNumber"]), + Time: common.Big(envValues["currentTimestamp"]), + Difficulty: common.Big(envValues["currentDifficulty"]), + GasLimit: common.Big(envValues["currentGasLimit"]), + GasPrice: common.Big(exeValues["gasPrice"]), + } - env.origin = common.HexToAddress(exeValues["caller"]) - env.parent = common.HexToHash(envValues["previousHash"]) - env.coinbase = common.HexToAddress(envValues["currentCoinbase"]) - env.number = common.Big(envValues["currentNumber"]) - env.time = common.Big(envValues["currentTimestamp"]) - env.difficulty = common.Big(envValues["currentDifficulty"]) - env.gasLimit = common.Big(envValues["currentGasLimit"]) - env.Gas = new(big.Int) - env.gasPrice = common.Big(exeValues["gasPrice"]) - - env.evm = vm.New(env, vm.Config{ - EnableJit: EnableJit, - ForceJit: ForceJit, - }) - - 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) GasPrice() *big.Int { return self.gasPrice } -func (self *Env) VmType() vm.Type { return vm.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 *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 { - if self.skipTransfer { - if self.initial { - self.initial = false - return true + //var initialCall bool + backend := &core.EVMBackend{ + GetHashFn: func(n uint64) common.Hash { + return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) + }, + State: state, + } + initialCall := true + canTransfer := func(db vm.Database, address common.Address, amount *big.Int) bool { + if vmTest { + if initialCall { + initialCall = false + return true + } } + return db.GetBalance(address).Cmp(amount) >= 0 } - - 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) { - if self.skipTransfer { - return + transfer := func(db vm.Database, sender, recipient common.Address, amount *big.Int) { + if vmTest { + return + } + core.Transfer(db, sender, recipient, amount) } - core.Transfer(from, to, amount) -} + context.CallContext = core.EVMCallContext{canTransfer, transfer} -func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - if self.vmTest && self.depth > 0 { - caller.ReturnGas(gas.Uint64()) - - return nil, nil - } - ret, err := core.Call(self, caller, addr, data, gas, value) - self.Gas = gas - - return ret, err - -} -func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) { - if self.vmTest && self.depth > 0 { - caller.ReturnGas(gas.Uint64()) - - return nil, nil - } - return core.CallCode(self, caller, addr, data, gas, value) -} - -func (self *Env) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) { - if self.vmTest && self.depth > 0 { - caller.ReturnGas(gas.Uint64()) - - return nil, nil - } - return core.DelegateCall(self, caller, addr, data, gas) -} - -func (self *Env) Create(caller vm.ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) { - if self.vmTest { - caller.ReturnGas(gas.Uint64()) - - nonce := self.state.GetNonce(caller.Address()) - obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce)) - - return nil, obj.Address(), nil - } else { - return core.Create(self, caller, data, gas, value) - } + env := vm.NewEnvironment(context, backend, chainConfig, vm.Config{Test: vmTest}) + return env } type Message struct { diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 53fc7a494f..07a92c64f8 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -211,7 +211,7 @@ func runVmTest(test VmTest) error { return nil } -func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) { +func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) { var ( to = common.HexToAddress(exec["address"]) from = common.HexToAddress(exec["caller"]) @@ -222,13 +222,11 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, // Reset the pre-compiled contracts for VM tests. vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract) - caller := state.GetOrNewStateObject(from) + caller := statedb.GetOrNewStateObject(from) - vmenv := NewEnvFromMap(¶ms.ChainConfig{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil, common.Hash{}, nil}, state, env, exec) - vmenv.vmTest = true - vmenv.skipTransfer = true - vmenv.initial = true + config := ¶ms.ChainConfig{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil, common.Hash{}, nil} + vmenv := NewEVMEnvironment(true, config, statedb, env, exec) ret, err := vmenv.Call(caller, to, data, gas, value) - return ret, vmenv.state.Logs(), vmenv.Gas, err + return ret, statedb.Logs(), gas, err }