diff --git a/cmd/evm/main.go b/cmd/evm/main.go index b1479d4f35..8150180908 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -28,8 +28,8 @@ 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" "github.com/ethereum/go-ethereum/logger/glog" "gopkg.in/urfave/cli.v1" @@ -44,33 +44,55 @@ 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", } 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", @@ -98,8 +120,6 @@ func init() { CreateFlag, DebugFlag, VerbosityFlag, - ForceJitFlag, - DisableJitFlag, SysStatFlag, CodeFlag, GasFlag, @@ -107,6 +127,11 @@ func init() { ValueFlag, DumpFlag, InputFlag, + GasLimitFlag, + CoinbaseFlag, + SenderFlag, + BlockNumberFlag, + BlockTimeFlag, } app.Action = run } @@ -116,19 +141,46 @@ func run(ctx *cli.Context) error { glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) logger := vm.NewStructLogger(nil) - vmenv := NewEnv(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, - }) - sender := vmenv.state.CreateAccount(common.StringToAddress("sender")) - 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, + ruleSet{}, + vm.Config{ + Debug: ctx.GlobalBool(DebugFlag.Name), + Tracer: logger, + }, + ) var ( - ret []byte - err error + ret []byte + tstart = time.Now() + sender = st.CreateAccount(context.Origin) ) if ctx.GlobalBool(CreateFlag.Name) { @@ -140,7 +192,7 @@ func run(ctx *cli.Context) error { common.Big(ctx.GlobalString(ValueFlag.Name)), ) } else { - receiver := vmenv.state.CreateAccount(common.StringToAddress("receiver")) + receiver := st.CreateAccount(common.StringToAddress("receiver")) receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))) ret, err = vmenv.Call( sender, @@ -153,8 +205,8 @@ func run(ctx *cli.Context) error { vmdone := time.Since(tstart) if ctx.GlobalBool(DumpFlag.Name) { - state.Commit(vmenv.state) - fmt.Println(string(vmenv.state.Dump())) + state.Commit(st) + fmt.Println(string(st.Dump())) } vm.StdErrFormat(logger.StructLogs()) @@ -186,99 +238,7 @@ func main() { } } -type VMEnv struct { - state *state.State - 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(transactor common.Address, value, price *big.Int, cfg vm.Config) *VMEnv { - db, err := ethdb.NewMemDatabase() - if err != nil { - panic(err) - } - st, err := state.New(common.Hash{}, db) - if err != nil { - panic(err) - } - - env := &VMEnv{ - state: st, - transactor: &transactor, - value: value, - price: price, - time: big.NewInt(time.Now().Unix()), - } - - env.evm = vm.New(env, cfg) - return env -} - // ruleSet implements vm.RuleSet and will always default to the homestead rule set. type ruleSet struct{} func (ruleSet) IsHomestead(*big.Int) bool { return true } - -func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Db() vm.Database { return self.state } -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) ForkState() vm.Database { - self.state = state.Fork(self.state) - return self.state -} -func (self *VMEnv) SetState(st vm.Database) { - self.state = st.(*state.State) -} -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 6dd0ef46f3..32fef80528 100644 --- a/common/registrar/ethreg/api.go +++ b/common/registrar/ethreg/api.go @@ -194,7 +194,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..61ef82d190 --- /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" +) + +// ToEVMContext creates a new context for use in the EVM. +func ToEVMContext(config *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.State +} + +// Fork forks the state and returns it. +func (b *EVMBackend) Fork() vm.Database { + b.State = state.Fork(b.State) + return b.State +} + +// Get returns the state +func (b *EVMBackend) Get() vm.Database { return b.State } + +// Set sets the current state +func (b *EVMBackend) Set(db vm.Database) { b.State = db.(*state.State) } + +// 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 d8ca0a2953..d887e071be 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(env, caller, &addr, &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(env, caller, &addr, &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(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, value) + ret, _, err = c.exec(env, caller, &callerAddr, &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, input, env.Db().GetCode(addr), gas, callerValue) + ret, _, err = c.execDelegateCall(env, caller, &originAddr, &callerAddr, &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(env, caller, nil, nil, 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(env, caller, nil, nil, 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(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, value *big.Int) (ret []byte, addr common.Address, err error) { +func (c EVMCallContext) exec(env *vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, 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())) @@ -87,7 +92,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A snapshotPreTransfer := env.Db() // preserve the envs state and move to a new state - env.ForkState() + env.Backend.Fork() var ( from = env.Db().GetAccount(caller.Address()) to vm.Account @@ -101,7 +106,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A to = env.Db().GetAccount(*address) } } - 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 // EVM. The contract is a scoped environment for this execution context @@ -128,26 +133,26 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // 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.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { + if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber) || err != vm.CodeStoreOutOfGasError) { contract.UseGas(contract.Gas()) - env.SetState(snapshotPreTransfer) + env.Backend.Set(snapshotPreTransfer) } return ret, addr, err } -func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, 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, 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.Db() - env.ForkState() + env.Backend.Fork() var to vm.Account if !env.Db().Exist(*toAddr) { @@ -165,14 +170,8 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA if err != nil { contract.UseGas(contract.Gas()) - env.SetState(snapshot) + env.Backend.Set(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 b49f248476..4fe0e9461e 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -65,6 +65,24 @@ func (self *State) RawDump() World { } world.Accounts[common.Bytes2Hex(addr)] = account } + for addr, stateObject := range self.StateObjects { + account := Account{ + Balance: stateObject.balance.String(), + Nonce: stateObject.nonce, + Root: common.Bytes2Hex(stateObject.Root()), + CodeHash: common.Bytes2Hex(stateObject.codeHash), + Code: common.Bytes2Hex(stateObject.Code()), + Storage: make(map[string]string), + } + storageIt := stateObject.trie.Iterator() + for storageIt.Next() { + account.Storage[common.Bytes2Hex(self.Trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) + } + for storageAddr, value := range stateObject.storage { + account.Storage[storageAddr.Hex()] = value.Hex() + } + world.Accounts[addr.Hex()] = account + } return world } diff --git a/core/state/state.go b/core/state/state.go index adda2f603e..21d8988f5f 100644 --- a/core/state/state.go +++ b/core/state/state.go @@ -133,6 +133,13 @@ func (s *State) CreateStateObject(address common.Address) *StateObject { return stateObject } +func (s *State) SubBalance(address common.Address, amount *big.Int) { + stateObject := s.GetOrNewStateObject(address) + if stateObject != nil { + stateObject.SubBalance(amount) + } +} + func (s *State) AddBalance(address common.Address, amount *big.Int) { stateObject := s.GetOrNewStateObject(address) if stateObject != nil { diff --git a/core/state_processor.go b/core/state_processor.go index 909c296206..e6f569c60e 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -99,28 +99,37 @@ func (p *StateProcessor) Process(block *types.Block, st *state.State, cfg vm.Con // ApplyTransactions returns the generated receipts and vm logs during the // execution of the state transition phase. func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, st *state.State, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*state.State, *types.Receipt, vm.Logs, *big.Int, error) { - env := NewEnv(st, config, bc, tx, header, cfg) + //env := NewEnv(st, config, bc, tx, header, cfg) + + backend := &EVMBackend{ + GetHashFn: GetHashFn(header.ParentHash, bc), + State: st, + } + context := ToEVMContext(config, tx, header) + + env := vm.NewEnvironment(context, backend, config, cfg) _, gas, err := ApplyMessage(env, tx, gp) if err != nil { - return env.state, nil, nil, nil, err + return env.Db().(*state.State), nil, nil, nil, err } + st = env.Db().(*state.State) // Update the state with pending changes usedGas.Add(usedGas, gas) - receipt := types.NewReceipt(state.IntermediateRoot(env.state).Bytes(), usedGas) + receipt := types.NewReceipt(state.IntermediateRoot(st).Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) if MessageCreatesContract(tx) { from, _ := tx.From() receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) } - receipt.Logs = env.state.Logs() + receipt.Logs = st.Logs() receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) glog.V(logger.Debug).Infoln(receipt) - return env.state, receipt, receipt.Logs, gas, err + return st, 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 bd3dac8846..864adb3b5a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -56,7 +56,7 @@ type StateTransition struct { value *big.Int data []byte - env vm.Environment + env *vm.Environment } // Message represents a message sent to a contract. @@ -105,7 +105,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, @@ -125,7 +125,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() @@ -138,7 +138,7 @@ func (self *StateTransition) from() (vm.Account, error) { err error st = self.env.Db() ) - if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) { + if self.env.RuleSet().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.RuleSet().IsHomestead(self.env.BlockNumber()) + homestead := self.env.RuleSet().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.env.Db().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 2abf690b95..0000000000 --- a/core/vm/environment.go +++ /dev/null @@ -1,131 +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" -) - -// 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 -} - -// Environment is an EVM requirement and helper which allows access to outside -// information such as states. -type Environment interface { - // The current ruleset - RuleSet() RuleSet - // The state database - Db() Database - // PreserveState preserve the current state and returns a new handle - ForkState() Database - // SetState sets the new state handle - SetState(Database) - - // Creates a restorable snapshot - //MakeSnapshot() Database - // Set database to previous snapshot - //SetSnapshot(Database) - // 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) - // 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) -} - -// Database is a EVM database for full state querying. -type Database interface { - GetAccount(common.Address) Account - CreateAccount(common.Address) Account - - AddBalance(common.Address, *big.Int) - GetBalance(common.Address) *big.Int - - GetNonce(common.Address) uint64 - SetNonce(common.Address, uint64) - - GetCode(common.Address) []byte - SetCode(common.Address, []byte) - - AddRefund(*big.Int) - GetRefund() *big.Int - - GetState(common.Address, common.Hash) common.Hash - SetState(common.Address, common.Hash, common.Hash) - - Delete(common.Address) bool - Exist(common.Address) bool - IsDeleted(common.Address) bool - - 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([]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 2e3dc02de9..b64a955426 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(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) + do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) // returns whether the program instruction halts the execution of the JIT halts() bool // Returns the current op code (debugging purposes) Op() OpCode } -type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) +type instrFn func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) type instruction struct { op OpCode @@ -58,7 +58,7 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract return mapping[to.Uint64()], nil } -func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (instr instruction) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // calculate the new memory size and gas price for the current executing opcode newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack) if err != nil { @@ -116,26 +116,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))) @@ -144,7 +144,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)) @@ -164,7 +164,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)) @@ -173,7 +173,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 { @@ -193,12 +193,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) @@ -215,12 +215,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)) @@ -229,7 +229,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)) @@ -238,7 +238,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)) @@ -247,7 +247,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)) @@ -256,7 +256,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)) @@ -265,7 +265,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)) @@ -274,19 +274,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()])) @@ -295,7 +295,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) @@ -305,7 +305,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) @@ -316,45 +316,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() @@ -363,18 +363,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(len(env.Db().GetCode(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() @@ -385,7 +385,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() @@ -397,58 +397,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() @@ -457,60 +457,60 @@ 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()) + log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64()) //env.AddLog(log) 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() @@ -523,7 +523,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.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { + if env.RuleSet().IsHomestead(env.BlockNumber) && suberr == CodeStoreOutOfGasError { stack.push(new(big.Int)) } else if suberr != nil && suberr != CodeStoreOutOfGasError { stack.push(new(big.Int)) @@ -532,7 +532,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() @@ -563,7 +563,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() @@ -594,7 +594,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) @@ -608,12 +608,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) @@ -624,7 +624,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++ { @@ -632,14 +632,14 @@ func makeLog(size int) instrFn { } d := memory.Get(mStart.Int64(), mSize.Int64()) - log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64()) + 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 @@ -648,7 +648,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)) } } @@ -657,7 +657,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..70f9717b93 --- /dev/null +++ b/core/vm/interface.go @@ -0,0 +1,191 @@ +// 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" +) + +// 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) + + GetCode(common.Address) []byte + SetCode(common.Address, []byte) + + AddRefund(*big.Int) + GetRefund() *big.Int + + GetState(common.Address, common.Hash) common.Hash + SetState(common.Address, common.Hash, common.Hash) + + Delete(common.Address) bool + Exist(common.Address) bool + IsDeleted(common.Address) bool + + 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([]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(n uint64) common.Hash // GetHash returns the hash corresponding to n + + Fork() Database // Fork returns a forked database + Get() Database // Get returns the current database + Set(Database) // Set sets the new database +} + +type Environment struct { + Context + + Backend Backend + + ruleSet RuleSet + vmConfig Config + + evm Vm + + Depth int +} + +func NewEnvironment(context Context, backend Backend, ruleSet RuleSet, vmCfg Config) *Environment { + env := &Environment{ + Context: context, + Backend: backend, + vmConfig: vmCfg, + ruleSet: ruleSet, + } + 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) +} +func (env *Environment) RuleSet() RuleSet { return env.ruleSet } +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 ae62b6b573..29225a84cb 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -149,7 +149,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) } diff --git a/core/vm/program.go b/core/vm/program.go index 4190ce82c1..14aba6e274 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 { // jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) { +func jitCalculateGasAndSize(env *Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (uint64, uint64, error) { var ( newMemSize uint64 sizeFault bool 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/segments.go b/core/vm/segments.go index 61d87367c6..ed223d82f2 100644 --- a/core/vm/segments.go +++ b/core/vm/segments.go @@ -24,7 +24,7 @@ type jumpSeg struct { gas uint64 } -func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (j jumpSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { if !contract.UseGas(j.gas) { return nil, OutOfGasError } @@ -42,7 +42,7 @@ type pushSeg struct { gas uint64 } -func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func (s pushSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Use the calculated gas. When insufficient gas is present, use all gas and return an // Out Of Gas error if !contract.UseGas(s.gas) { diff --git a/core/vm/vm.go b/core/vm/vm.go index e5807d143a..40c09fff6a 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -27,10 +27,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 @@ -38,24 +40,24 @@ 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 } // 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.RuleSet(), env.BlockNumber()), + jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber), cfg: cfg, } } // 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 { @@ -80,6 +82,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: @@ -111,7 +116,7 @@ func (evm *EVM) runProgram(program *Program, contract *Contract, input []byte) ( }() } - homestead := env.RuleSet().IsHomestead(env.BlockNumber()) + homestead := env.RuleSet().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 cd2a041f50..0000000000 --- a/core/vm_env.go +++ /dev/null @@ -1,121 +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" -) - -// 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 *ChainConfig // Chain configuration - - state *state.State - - evm vm.VirtualMachine // 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.State, chainConfig *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) RuleSet() vm.RuleSet { 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) ForkState() vm.Database { - self.state = state.Fork(self.state) - return self.state -} - -func (self *VMEnv) SetState(st vm.Database) { - self.state = st.(*state.State) -} - -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 f4bce47b84..df95f4562c 100644 --- a/eth/api.go +++ b/eth/api.go @@ -520,9 +520,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) @@ -531,7 +539,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 fa0365f7d3..481a9ee02f 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -97,13 +97,21 @@ 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, st ethapi.State, header *types.Header) (vm.Environment, func() error, error) { +func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, st ethapi.State, header *types.Header) (*vm.Environment, func() error, error) { stateDb := state.Fork(st.(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, b.eth.chainConfig.VmConfig), 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, b.eth.chainConfig.VmConfig), vmError, nil + //return core.NewEnv(stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil } func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/eth/backend.go b/eth/backend.go index c4a883c9e9..a9e1630d33 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -35,7 +35,6 @@ import ( "github.com/ethereum/go-ethereum/common/registrar/ethreg" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -202,10 +201,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig) eth.chainConfig = config.ChainConfig - eth.chainConfig.VmConfig = vm.Config{ - EnableJit: config.EnableJit, - ForceJit: config.ForceJit, - } eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) if err != nil { 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/miner/worker.go b/miner/worker.go index 85b9f7f958..19898ca778 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -620,13 +620,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { // this is a bit of a hack to force jit for the miners - config := env.config.VmConfig - if !(config.EnableJit && config.ForceJit) { - config.EnableJit = false - } - config.ForceJit = false // disable forcing jit - - state, receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config) + state, receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, env.config.VmConfig) defer func() { env.state = state }() if err != nil { diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 684cec4bf6..ae97437f80 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -236,14 +236,14 @@ func RunState(ruleSet RuleSet, statedb *state.State, env, tx map[string]string) key, _ := hex.DecodeString(tx["secretKey"]) addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) message := NewMessage(addr, to, data, value, gas, price, nonce) - vmenv := NewEnvFromMap(ruleSet, nstatedb, env, tx) - vmenv.origin = addr + vmenv := NewEVMEnvironment(false, ruleSet, nstatedb, env, tx) + //vmenv.origin = addr ret, _, err := core.ApplyMessage(vmenv, message, gaspool) if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { - vmenv.SetState(snapshot) + vmenv.Backend.Set(snapshot) } - statedb.Set(state.Flatten(vmenv.state)) + statedb.Set(state.Flatten(vmenv.Db().(*state.State))) state.Commit(statedb) - return ret, statedb.Logs(), vmenv.Gas, err + return ret, statedb.Logs(), gas, err } diff --git a/tests/util.go b/tests/util.go index a1c29baa77..4f4ef84c84 100644 --- a/tests/util.go +++ b/tests/util.go @@ -18,6 +18,7 @@ package tests import ( "bytes" + "encoding/hex" "fmt" "math/big" "os" @@ -149,143 +150,52 @@ func (r RuleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.HomesteadBlock) >= 0 } -type Env struct { - ruleSet RuleSet - depth int - state *state.State - skipTransfer bool - initial bool - Gas, gasPrice *big.Int +func NewEVMEnvironment(vmTest bool, ruleSet RuleSet, state *state.State, envValues map[string]string, exeValues map[string]string) *vm.Environment { + //env := NewEnv(ruleSet, state) - 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 -} - -func NewEnv(ruleSet RuleSet, state *state.State) *Env { - env := &Env{ - ruleSet: ruleSet, - state: state, + 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(ruleSet RuleSet, state *state.State, envValues map[string]string, exeValues map[string]string) *Env { - env := NewEnv(ruleSet, 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) RuleSet() vm.RuleSet { return self.ruleSet } -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) ForkState() vm.Database { - self.state = state.Fork(self.state) - return self.state -} - -func (self *Env) SetState(st vm.Database) { - self.state = st.(*state.State) -} - -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, ruleSet, vm.Config{Test: vmTest}) + return env } type Message struct { diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 08545407d3..58da6feae1 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -240,14 +240,11 @@ func RunVm(statedb *state.State, env, exec map[string]string) ([]byte, vm.Logs, caller := statedb.GetOrNewStateObject(from) - vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true}, statedb, env, exec) - vmenv.vmTest = true - vmenv.skipTransfer = true - vmenv.initial = true + vmenv := NewEVMEnvironment(true, RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true}, statedb, env, exec) ret, err := vmenv.Call(caller, to, data, gas, value) - statedb.Set(state.Flatten(vmenv.state)) + statedb.Set(state.Flatten(vmenv.Db().(*state.State))) state.Commit(statedb) - return ret, statedb.Logs(), vmenv.Gas, err + return ret, statedb.Logs(), gas, err }