From 3fc7c978277051391f8ea7831559e9f4f83c3166 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Tue, 6 Dec 2016 02:16:03 +0100 Subject: core, core/vm: implemented a generic environment (#3348) Environment is now a struct (not an interface). This reduces a lot of tech-debt throughout the codebase where a virtual machine environment had to be implemented in order to test or run it. The new environment is suitable to be used en the json tests, core consensus and light client. --- light/odr_test.go | 13 ++++-- light/state.go | 9 ++++ light/state_object.go | 2 +- light/vm_env.go | 119 +++++++++----------------------------------------- 4 files changed, 40 insertions(+), 103 deletions(-) (limited to 'light') diff --git a/light/odr_test.go b/light/odr_test.go index 50255a7f3..2f60f32fd 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -157,6 +157,8 @@ func (callmsg) CheckNonce() bool { return false } func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte { data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") + config := params.TestChainConfig + var res []byte for i := 0; i < 3; i++ { data[35] = byte(i) @@ -168,7 +170,10 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain from.SetBalance(common.MaxBig) msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)} - vmenv := core.NewEnv(statedb, testChainConfig(), bc, msg, header, vm.Config{}) + + context := core.NewEVMContext(msg, header, bc) + vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{}) + gp := new(core.GasPool).AddGas(common.MaxBig) ret, _, _ := core.ApplyMessage(vmenv, msg, gp) res = append(res, ret...) @@ -176,15 +181,17 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain } else { header := lc.GetHeaderByHash(bhash) state := NewLightState(StateTrieID(header), lc.Odr()) + vmstate := NewVMState(ctx, state) from, err := state.GetOrNewStateObject(ctx, testBankAddress) if err == nil { from.SetBalance(common.MaxBig) msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)} - vmenv := NewEnv(ctx, state, testChainConfig(), lc, msg, header, vm.Config{}) + context := core.NewEVMContext(msg, header, lc) + vmenv := vm.NewEnvironment(context, vmstate, config, vm.Config{}) gp := new(core.GasPool).AddGas(common.MaxBig) ret, _, _ := core.ApplyMessage(vmenv, msg, gp) - if vmenv.Error() == nil { + if vmstate.Error() == nil { res = append(res, ret...) } } diff --git a/light/state.go b/light/state.go index 9f2376809..f8b75c588 100644 --- a/light/state.go +++ b/light/state.go @@ -141,6 +141,15 @@ func (self *LightState) AddBalance(ctx context.Context, addr common.Address, amo return err } +// SubBalance adds the given amount to the balance of the specified account +func (self *LightState) SubBalance(ctx context.Context, addr common.Address, amount *big.Int) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.SubBalance(amount) + } + return err +} + // SetNonce sets the nonce of the specified account func (self *LightState) SetNonce(ctx context.Context, addr common.Address, nonce uint64) error { stateObject, err := self.GetOrNewStateObject(ctx, addr) diff --git a/light/state_object.go b/light/state_object.go index 6161d2dfb..56f607bff 100644 --- a/light/state_object.go +++ b/light/state_object.go @@ -179,7 +179,7 @@ func (c *StateObject) SetBalance(amount *big.Int) { } // ReturnGas returns the gas back to the origin. Used by the Virtual machine or Closures -func (c *StateObject) ReturnGas(gas, price *big.Int) {} +func (c *StateObject) ReturnGas(gas *big.Int) {} // Copy creates a copy of the state object func (self *StateObject) Copy() *StateObject { diff --git a/light/vm_env.go b/light/vm_env.go index d4d7bcce7..cc0c568c9 100644 --- a/light/vm_env.go +++ b/light/vm_env.go @@ -20,123 +20,38 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "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/crypto" - "github.com/ethereum/go-ethereum/params" "golang.org/x/net/context" ) -// VMEnv is the light client version of the vm execution environment. -// Unlike other structures, VMEnv holds a context that is applied by state -// retrieval requests through the entire execution. If any state operation -// returns an error, the execution fails. -type VMEnv struct { - vm.Environment - ctx context.Context - chainConfig *params.ChainConfig - evm *vm.EVM - state *VMState - header *types.Header - msg core.Message - depth int - chain *LightChain - err error -} - -// NewEnv creates a new execution environment based on an ODR capable light state -func NewEnv(ctx context.Context, state *LightState, chainConfig *params.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv { - env := &VMEnv{ - chainConfig: chainConfig, - chain: chain, - header: header, - msg: msg, - } - env.state = &VMState{ctx: ctx, state: state, env: env} - - env.evm = vm.New(env, cfg) - return env -} - -func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Origin() common.Address { return self.msg.From() } -func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } -func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } -func (self *VMEnv) Time() *big.Int { return self.header.Time } -func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } -func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } -func (self *VMEnv) 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 { - for header := self.chain.GetHeader(self.header.ParentHash, self.header.Number.Uint64()-1); header != nil; header = self.chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { - if header.Number.Uint64() == n { - return header.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) SnapshotDatabase() int { - return self.state.SnapshotDatabase() -} - -func (self *VMEnv) RevertToSnapshot(idx int) { - self.state.RevertToSnapshot(idx) -} - -func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) { - core.Transfer(from, to, amount) -} - -func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return core.Call(self, me, addr, data, gas, price, value) -} -func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { - return core.CallCode(self, me, addr, data, gas, price, value) -} - -func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { - return core.DelegateCall(self, me, addr, data, gas, price) -} - -func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { - return core.Create(self, me, data, gas, price, value) -} - -// Error returns the error (if any) that happened during execution. -func (self *VMEnv) Error() error { - return self.err -} - // VMState is a wrapper for the light state that holds the actual context and // passes it to any state operation that requires it. type VMState struct { - vm.Database ctx context.Context state *LightState snapshots []*LightState - env *VMEnv + err error +} + +func NewVMState(ctx context.Context, state *LightState) *VMState { + return &VMState{ctx: ctx, state: state} +} + +func (s *VMState) Error() error { + return s.err } +func (s *VMState) AddLog(log *vm.Log) {} + // errHandler handles and stores any state error that happens during execution. func (s *VMState) errHandler(err error) { - if err != nil && s.env.err == nil { - s.env.err = err + if err != nil && s.err == nil { + s.err = err } } -func (self *VMState) SnapshotDatabase() int { +func (self *VMState) Snapshot() int { self.snapshots = append(self.snapshots, self.state.Copy()) return len(self.snapshots) - 1 } @@ -175,6 +90,12 @@ func (s *VMState) AddBalance(addr common.Address, amount *big.Int) { s.errHandler(err) } +// SubBalance adds the given amount to the balance of the specified account +func (s *VMState) SubBalance(addr common.Address, amount *big.Int) { + err := s.state.SubBalance(s.ctx, addr, amount) + s.errHandler(err) +} + // GetBalance retrieves the balance from the given address or 0 if the account does // not exist func (s *VMState) GetBalance(addr common.Address) *big.Int { -- cgit v1.2.3