diff --git a/core/blockchain.go b/core/blockchain.go index 85765bb45c..63412a1e43 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -205,6 +205,20 @@ func (self *BlockChain) loadLastState() error { glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd) glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd) + /* + st, err := state.New(currentHeader.Root, self.chainDb) + if err != nil { + return err + } + glog.V(logger.Info).Infoln("caching state accounts...") + tstart := time.Now() + if err := st.LoadAll(); err != nil { + return fmt.Errorf("core: could not pre-load account data: %v", err) + } + self.canonState = st + glog.V(logger.Info).Infoln("cached", len(st.StateObjects), "in", time.Since(tstart)) + */ + return nil } diff --git a/core/state/state.go b/core/state/state.go index 43ecebc9fd..8ccc68dbb6 100644 --- a/core/state/state.go +++ b/core/state/state.go @@ -1,6 +1,7 @@ package state import ( + "bytes" "fmt" "math/big" @@ -53,6 +54,19 @@ func New(root common.Hash, db ethdb.Database) (*State, error) { }, nil } +func (s *State) LoadAll() error { + it := s.Trie.Iterator() + for it.Next() { + addr := s.Trie.GetKey(it.Key) + obj, err := DecodeObjectLazily(common.BytesToAddress(addr), s.Db, it.Value) + if err != nil { + return err + } + s.StateObjects[common.BytesToAddress(addr)] = obj + } + return nil +} + func (s *State) PrepareIntermediate(txHash, blockHash common.Hash, txIdx int) { if s.parent != nil { s.parent.MarkedTransition = true @@ -79,6 +93,7 @@ func (s *State) Read(address common.Address) (object *StateObject, inCache bool) if stateObject.deleted { return nil, false } + s.loadStateObject(stateObject) return stateObject, inCache } @@ -96,6 +111,20 @@ func (s *State) Read(address common.Address) (object *StateObject, inCache bool) return stateObject, false } +func (s *State) loadStateObject(stateObject *StateObject) { + if stateObject.trie == nil { + var err error + if stateObject.trie, err = trie.NewSecure(stateObject.root, s.Db); err != nil { + panic(err) + } + if !bytes.Equal(stateObject.codeHash, emptyCodeHash) { + if stateObject.code, err = s.Db.Get(stateObject.codeHash); err != nil { + panic(fmt.Sprintf("can't get code for hash %x: %v", stateObject.codeHash, err)) + } + } + } +} + func (s *State) GetOrNewStateObject(address common.Address) *StateObject { stateObject, inCache := s.Read(address) if stateObject != nil { @@ -150,6 +179,7 @@ func (s *State) CreateStateObject(address common.Address) *StateObject { stateObject.SetNonce(StartingNonce) s.StateObjects[address] = stateObject + s.localStateObjects[address] = true // If it existed set the balance to the new account if so != nil { @@ -247,8 +277,15 @@ func (s *State) SetState(address common.Address, stateAddress common.Hash, value func (s *State) Delete(address common.Address) bool { stateObject := s.GetStateObject(address) if stateObject != nil { - stateObject.MarkForDeletion() + if !s.localStateObjects[address] { + stateObject = stateObject.Copy() + s.StateObjects[address] = stateObject + s.localStateObjects[address] = true + } + + stateObject.remove = true stateObject.balance = new(big.Int) + stateObject.dirty = true return true } @@ -311,6 +348,8 @@ func (s *State) DeleteStateObject(stateObject *StateObject) { } func (s *State) UpdateStateObject(stateObject *StateObject) { + s.loadStateObject(stateObject) + addr := stateObject.Address() data, err := rlp.EncodeToBytes(stateObject) if err != nil { @@ -320,7 +359,6 @@ func (s *State) UpdateStateObject(stateObject *StateObject) { } func (s *State) Reset(root common.Hash) error { - fmt.Println("reset") var ( err error tr = s.Trie @@ -390,20 +428,10 @@ func (s *State) Set(o *State) { // Flatten flattens the state in to a single new state, including all changes of all ancestors. func Flatten(s *State) *State { - // first commit the parent so we can overwrite changes - // later. - var flattenedState *State - if s.parent != nil { - flattenedState = Flatten(s.parent) - } else { - flattenedState = &State{ - Db: s.Db, - Trie: s.Trie, - refund: new(big.Int), - StateObjects: make(map[common.Address]*StateObject), - ownedStateObjects: make(map[common.Address]bool), - } + if s.parent == nil { + return s } + flattenedState := Flatten(s.parent) for address, object := range s.StateObjects { flattenedState.StateObjects[address] = object @@ -425,7 +453,8 @@ func (s *State) String() string { func IntermediateRoot(state *State) common.Hash { s := Flatten(state) - for _, stateObject := range s.StateObjects { + for address, _ := range s.localStateObjects { + stateObject := s.StateObjects[address] if stateObject.dirty { if stateObject.remove { s.DeleteStateObject(stateObject) diff --git a/core/state/state_object.go b/core/state/state_object.go index a8a67d09ba..4bc70c1b43 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -80,7 +80,8 @@ type StateObject struct { remove bool deleted bool dirty bool - written bool + + root common.Hash } func NewStateObject(address common.Address, db trie.Database) *StateObject { @@ -99,7 +100,6 @@ func NewStateObject(address common.Address, db trie.Database) *StateObject { func (self *StateObject) MarkForDeletion() { self.remove = true self.dirty = true - self.written = false if glog.V(logger.Core) { glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance) @@ -140,7 +140,6 @@ func (self *StateObject) GetState(key common.Hash) common.Hash { func (self *StateObject) SetState(key, value common.Hash) { self.storage[key] = value self.dirty = true - self.written = false } // Update updates the current cached storage to the trie @@ -160,7 +159,6 @@ func (c *StateObject) AddBalance(amount *big.Int) { if glog.V(logger.Core) { glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount) } - c.written = false } func (c *StateObject) SubBalance(amount *big.Int) { @@ -169,13 +167,11 @@ func (c *StateObject) SubBalance(amount *big.Int) { if glog.V(logger.Core) { glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount) } - c.written = false } func (c *StateObject) SetBalance(amount *big.Int) { c.balance = amount c.dirty = true - c.written = false } func (c *StateObject) St() Storage { @@ -229,13 +225,11 @@ func (self *StateObject) SetCode(code []byte) { self.code = code self.codeHash = crypto.Keccak256(code) self.dirty = true - self.written = false } func (self *StateObject) SetNonce(nonce uint64) { self.nonce = nonce self.dirty = true - self.written = false } func (self *StateObject) Nonce() uint64 { @@ -300,3 +294,20 @@ func DecodeObject(address common.Address, db trie.Database, data []byte) (*State obj.codeHash = ext.CodeHash return obj, nil } + +// DecodeObjectLazily decodes an RLP-encoded state object. +func DecodeObjectLazily(address common.Address, db trie.Database, data []byte) (*StateObject, error) { + var ( + obj = &StateObject{address: address, db: db, storage: make(Storage)} + ext extStateObject + err error + ) + if err = rlp.DecodeBytes(data, &ext); err != nil { + return nil, err + } + obj.root = ext.Root + obj.nonce = ext.Nonce + obj.balance = ext.Balance + obj.codeHash = ext.CodeHash + return obj, nil +} diff --git a/core/state/state_write.go b/core/state/state_write.go index 73faf06b94..e260e101ed 100644 --- a/core/state/state_write.go +++ b/core/state/state_write.go @@ -33,7 +33,9 @@ func stateCommit(state *State, db trie.DatabaseWriter) (common.Hash, error) { // and just mark it for deletion in the trie. stateObject.deleted = true state.Trie.Delete(stateObject.Address().Bytes()[:]) - } else if !stateObject.written { + // Delete the object from the cache. + // delete(state.StateObjects, address) + } else { // Write any contract code associated with the state object if len(stateObject.code) > 0 { if err := db.Put(stateObject.codeHash, stateObject.code); err != nil { @@ -60,7 +62,6 @@ func stateCommit(state *State, db trie.DatabaseWriter) (common.Hash, error) { } stateObject.dirty = false - stateObject.written = true } return state.Trie.CommitTo(db) }