diff options
Diffstat (limited to 'light/state.go')
-rw-r--r-- | light/state.go | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/light/state.go b/light/state.go new file mode 100644 index 000000000..e18f9cdc5 --- /dev/null +++ b/light/state.go @@ -0,0 +1,275 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package light + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "golang.org/x/net/context" +) + +// StartingNonce determines the default nonce when new accounts are being created. +var StartingNonce uint64 + +// LightState is a memory representation of a state. +// This version is ODR capable, caching only the already accessed part of the +// state, retrieving unknown parts on-demand from the ODR backend. Changes are +// never stored in the local database, only in the memory objects. +type LightState struct { + odr OdrBackend + trie *LightTrie + + stateObjects map[string]*StateObject +} + +// NewLightState creates a new LightState with the specified root. +// Note that the creation of a light state is always successful, even if the +// root is non-existent. In that case, ODR retrieval will always be unsuccessful +// and every operation will return with an error or wait for the context to be +// cancelled. +func NewLightState(root common.Hash, odr OdrBackend) *LightState { + tr := NewLightTrie(root, odr, true) + return &LightState{ + odr: odr, + trie: tr, + stateObjects: make(map[string]*StateObject), + } +} + +// HasAccount returns true if an account exists at the given address +func (self *LightState) HasAccount(ctx context.Context, addr common.Address) (bool, error) { + so, err := self.GetStateObject(ctx, addr) + return so != nil, err +} + +// GetBalance retrieves the balance from the given address or 0 if the account does +// not exist +func (self *LightState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err != nil { + return common.Big0, err + } + if stateObject != nil { + return stateObject.balance, nil + } + + return common.Big0, nil +} + +// GetNonce returns the nonce at the given address or 0 if the account does +// not exist +func (self *LightState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err != nil { + return 0, err + } + if stateObject != nil { + return stateObject.nonce, nil + } + return 0, nil +} + +// GetCode returns the contract code at the given address or nil if the account +// does not exist +func (self *LightState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err != nil { + return nil, err + } + if stateObject != nil { + return stateObject.code, nil + } + return nil, nil +} + +// GetState returns the contract storage value at storage address b from the +// contract address a or common.Hash{} if the account does not exist +func (self *LightState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) { + stateObject, err := self.GetStateObject(ctx, a) + if err == nil && stateObject != nil { + return stateObject.GetState(ctx, b) + } + return common.Hash{}, err +} + +// IsDeleted returns true if the given account has been marked for deletion +// or false if the account does not exist +func (self *LightState) IsDeleted(ctx context.Context, addr common.Address) (bool, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err == nil && stateObject != nil { + return stateObject.remove, nil + } + return false, err +} + +/* + * SETTERS + */ + +// AddBalance adds the given amount to the balance of the specified account +func (self *LightState) AddBalance(ctx context.Context, addr common.Address, amount *big.Int) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.AddBalance(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) + if err == nil && stateObject != nil { + stateObject.SetNonce(nonce) + } + return err +} + +// SetCode sets the contract code at the specified account +func (self *LightState) SetCode(ctx context.Context, addr common.Address, code []byte) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.SetCode(code) + } + return err +} + +// SetState sets the storage value at storage address key of the account addr +func (self *LightState) SetState(ctx context.Context, addr common.Address, key common.Hash, value common.Hash) error { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.SetState(key, value) + } + return err +} + +// Delete marks an account to be removed and clears its balance +func (self *LightState) Delete(ctx context.Context, addr common.Address) (bool, error) { + stateObject, err := self.GetOrNewStateObject(ctx, addr) + if err == nil && stateObject != nil { + stateObject.MarkForDeletion() + stateObject.balance = new(big.Int) + + return true, nil + } + + return false, err +} + +// +// Get, set, new state object methods +// + +// GetStateObject returns the state object of the given account or nil if the +// account does not exist +func (self *LightState) GetStateObject(ctx context.Context, addr common.Address) (stateObject *StateObject, err error) { + stateObject = self.stateObjects[addr.Str()] + if stateObject != nil { + if stateObject.deleted { + stateObject = nil + } + return stateObject, nil + } + data, err := self.trie.Get(ctx, addr[:]) + if err != nil { + return nil, err + } + if len(data) == 0 { + return nil, nil + } + + stateObject, err = DecodeObject(ctx, addr, self.odr, []byte(data)) + if err != nil { + return nil, err + } + + self.SetStateObject(stateObject) + + return stateObject, nil +} + +// SetStateObject sets the state object of the given account +func (self *LightState) SetStateObject(object *StateObject) { + self.stateObjects[object.Address().Str()] = object +} + +// GetOrNewStateObject returns the state object of the given account or creates a +// new one if the account does not exist +func (self *LightState) GetOrNewStateObject(ctx context.Context, addr common.Address) (*StateObject, error) { + stateObject, err := self.GetStateObject(ctx, addr) + if err == nil && (stateObject == nil || stateObject.deleted) { + stateObject, err = self.CreateStateObject(ctx, addr) + } + return stateObject, err +} + +// newStateObject creates a state object whether it exists in the state or not +func (self *LightState) newStateObject(addr common.Address) *StateObject { + if glog.V(logger.Core) { + glog.Infof("(+) %x\n", addr) + } + + stateObject := NewStateObject(addr, self.odr) + stateObject.SetNonce(StartingNonce) + self.stateObjects[addr.Str()] = stateObject + + return stateObject +} + +// CreateStateObject creates creates a new state object and takes ownership. +// This is different from "NewStateObject" +func (self *LightState) CreateStateObject(ctx context.Context, addr common.Address) (*StateObject, error) { + // Get previous (if any) + so, err := self.GetStateObject(ctx, addr) + if err != nil { + return nil, err + } + // Create a new one + newSo := self.newStateObject(addr) + + // If it existed set the balance to the new account + if so != nil { + newSo.balance = so.balance + } + + return newSo, nil +} + +// +// Setting, copying of the state methods +// + +// Copy creates a copy of the state +func (self *LightState) Copy() *LightState { + // ignore error - we assume state-to-be-copied always exists + state := NewLightState(common.Hash{}, self.odr) + state.trie = self.trie + for k, stateObject := range self.stateObjects { + state.stateObjects[k] = stateObject.Copy() + } + + return state +} + +// Set copies the contents of the given state onto this state, overwriting +// its contents +func (self *LightState) Set(state *LightState) { + self.trie = state.trie + self.stateObjects = state.stateObjects +} |