core, core/state: improved update loops

This commit is contained in:
Jeffrey Wilcke 2016-09-24 16:42:15 +02:00
parent 91ecef628a
commit 08aeea24ba
4 changed files with 81 additions and 26 deletions

View file

@ -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("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) 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 return nil
} }

View file

@ -1,6 +1,7 @@
package state package state
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
@ -53,6 +54,19 @@ func New(root common.Hash, db ethdb.Database) (*State, error) {
}, nil }, 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) { func (s *State) PrepareIntermediate(txHash, blockHash common.Hash, txIdx int) {
if s.parent != nil { if s.parent != nil {
s.parent.MarkedTransition = true s.parent.MarkedTransition = true
@ -79,6 +93,7 @@ func (s *State) Read(address common.Address) (object *StateObject, inCache bool)
if stateObject.deleted { if stateObject.deleted {
return nil, false return nil, false
} }
s.loadStateObject(stateObject)
return stateObject, inCache return stateObject, inCache
} }
@ -96,6 +111,20 @@ func (s *State) Read(address common.Address) (object *StateObject, inCache bool)
return stateObject, false 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 { func (s *State) GetOrNewStateObject(address common.Address) *StateObject {
stateObject, inCache := s.Read(address) stateObject, inCache := s.Read(address)
if stateObject != nil { if stateObject != nil {
@ -150,6 +179,7 @@ func (s *State) CreateStateObject(address common.Address) *StateObject {
stateObject.SetNonce(StartingNonce) stateObject.SetNonce(StartingNonce)
s.StateObjects[address] = stateObject s.StateObjects[address] = stateObject
s.localStateObjects[address] = true
// If it existed set the balance to the new account // If it existed set the balance to the new account
if so != nil { 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 { func (s *State) Delete(address common.Address) bool {
stateObject := s.GetStateObject(address) stateObject := s.GetStateObject(address)
if stateObject != nil { 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.balance = new(big.Int)
stateObject.dirty = true
return true return true
} }
@ -311,6 +348,8 @@ func (s *State) DeleteStateObject(stateObject *StateObject) {
} }
func (s *State) UpdateStateObject(stateObject *StateObject) { func (s *State) UpdateStateObject(stateObject *StateObject) {
s.loadStateObject(stateObject)
addr := stateObject.Address() addr := stateObject.Address()
data, err := rlp.EncodeToBytes(stateObject) data, err := rlp.EncodeToBytes(stateObject)
if err != nil { if err != nil {
@ -320,7 +359,6 @@ func (s *State) UpdateStateObject(stateObject *StateObject) {
} }
func (s *State) Reset(root common.Hash) error { func (s *State) Reset(root common.Hash) error {
fmt.Println("reset")
var ( var (
err error err error
tr = s.Trie 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. // Flatten flattens the state in to a single new state, including all changes of all ancestors.
func Flatten(s *State) *State { func Flatten(s *State) *State {
// first commit the parent so we can overwrite changes if s.parent == nil {
// later. return s
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),
}
} }
flattenedState := Flatten(s.parent)
for address, object := range s.StateObjects { for address, object := range s.StateObjects {
flattenedState.StateObjects[address] = object flattenedState.StateObjects[address] = object
@ -425,7 +453,8 @@ func (s *State) String() string {
func IntermediateRoot(state *State) common.Hash { func IntermediateRoot(state *State) common.Hash {
s := Flatten(state) s := Flatten(state)
for _, stateObject := range s.StateObjects { for address, _ := range s.localStateObjects {
stateObject := s.StateObjects[address]
if stateObject.dirty { if stateObject.dirty {
if stateObject.remove { if stateObject.remove {
s.DeleteStateObject(stateObject) s.DeleteStateObject(stateObject)

View file

@ -80,7 +80,8 @@ type StateObject struct {
remove bool remove bool
deleted bool deleted bool
dirty bool dirty bool
written bool
root common.Hash
} }
func NewStateObject(address common.Address, db trie.Database) *StateObject { 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() { func (self *StateObject) MarkForDeletion() {
self.remove = true self.remove = true
self.dirty = true self.dirty = true
self.written = false
if glog.V(logger.Core) { if glog.V(logger.Core) {
glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance) 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) { func (self *StateObject) SetState(key, value common.Hash) {
self.storage[key] = value self.storage[key] = value
self.dirty = true self.dirty = true
self.written = false
} }
// Update updates the current cached storage to the trie // 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) { if glog.V(logger.Core) {
glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount) glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount)
} }
c.written = false
} }
func (c *StateObject) SubBalance(amount *big.Int) { func (c *StateObject) SubBalance(amount *big.Int) {
@ -169,13 +167,11 @@ func (c *StateObject) SubBalance(amount *big.Int) {
if glog.V(logger.Core) { if glog.V(logger.Core) {
glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount) glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount)
} }
c.written = false
} }
func (c *StateObject) SetBalance(amount *big.Int) { func (c *StateObject) SetBalance(amount *big.Int) {
c.balance = amount c.balance = amount
c.dirty = true c.dirty = true
c.written = false
} }
func (c *StateObject) St() Storage { func (c *StateObject) St() Storage {
@ -229,13 +225,11 @@ func (self *StateObject) SetCode(code []byte) {
self.code = code self.code = code
self.codeHash = crypto.Keccak256(code) self.codeHash = crypto.Keccak256(code)
self.dirty = true self.dirty = true
self.written = false
} }
func (self *StateObject) SetNonce(nonce uint64) { func (self *StateObject) SetNonce(nonce uint64) {
self.nonce = nonce self.nonce = nonce
self.dirty = true self.dirty = true
self.written = false
} }
func (self *StateObject) Nonce() uint64 { func (self *StateObject) Nonce() uint64 {
@ -300,3 +294,20 @@ func DecodeObject(address common.Address, db trie.Database, data []byte) (*State
obj.codeHash = ext.CodeHash obj.codeHash = ext.CodeHash
return obj, nil 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
}

View file

@ -33,7 +33,9 @@ func stateCommit(state *State, db trie.DatabaseWriter) (common.Hash, error) {
// and just mark it for deletion in the trie. // and just mark it for deletion in the trie.
stateObject.deleted = true stateObject.deleted = true
state.Trie.Delete(stateObject.Address().Bytes()[:]) 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 // Write any contract code associated with the state object
if len(stateObject.code) > 0 { if len(stateObject.code) > 0 {
if err := db.Put(stateObject.codeHash, stateObject.code); err != nil { 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.dirty = false
stateObject.written = true
} }
return state.Trie.CommitTo(db) return state.Trie.CommitTo(db)
} }