@@ -28,23 +28,32 @@ import (
2828 "github.com/ethereum/go-ethereum/logger/glog"
2929 "github.com/ethereum/go-ethereum/rlp"
3030 "github.com/ethereum/go-ethereum/trie"
31+ lru "github.com/hashicorp/golang-lru"
3132)
3233
3334// The starting nonce determines the default nonce when new accounts are being
3435// created.
3536var StartingNonce uint64
3637
38+ const (
39+ // Number of past tries to keep. The arbitrarily chosen value here
40+ // is max uncle depth + 1.
41+ maxJournalLength = 8
42+
43+ // Number of codehash->size associations to keep.
44+ codeSizeCacheSize = 100000
45+ )
46+
3747// StateDBs within the ethereum protocol are used to store anything
3848// within the merkle trie. StateDBs take care of caching and storing
3949// nested states. It's the general query interface to retrieve:
4050// * Contracts
4151// * Accounts
4252type StateDB struct {
43- db ethdb.Database
44- trie * trie.SecureTrie
45-
46- // This map caches canon state accounts.
47- all map [common.Address ]Account
53+ db ethdb.Database
54+ trie * trie.SecureTrie
55+ pastTries []* trie.SecureTrie
56+ codeSizeCache * lru.Cache
4857
4958 // This map holds 'live' objects, which will get modified while processing a state transition.
5059 stateObjects map [common.Address ]* StateObject
@@ -65,10 +74,11 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
6574 if err != nil {
6675 return nil , err
6776 }
77+ csc , _ := lru .New (codeSizeCacheSize )
6878 return & StateDB {
6979 db : db ,
7080 trie : tr ,
71- all : make ( map [common. Address ] Account ) ,
81+ codeSizeCache : csc ,
7282 stateObjects : make (map [common.Address ]* StateObject ),
7383 stateObjectsDirty : make (map [common.Address ]struct {}),
7484 refund : new (big.Int ),
@@ -79,19 +89,15 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
7989// Reset clears out all emphemeral state objects from the state db, but keeps
8090// the underlying state trie to avoid reloading data for the next operations.
8191func (self * StateDB ) Reset (root common.Hash ) error {
82- tr , err := trie . NewSecure (root , self . db )
92+ tr , err := self . openTrie (root )
8393 if err != nil {
8494 return err
8595 }
86- all := self .all
87- if self .trie .Hash () != root {
88- // The root has changed, invalidate canon state.
89- all = make (map [common.Address ]Account )
90- }
9196 * self = StateDB {
9297 db : self .db ,
9398 trie : tr ,
94- all : all ,
99+ pastTries : self .pastTries ,
100+ codeSizeCache : self .codeSizeCache ,
95101 stateObjects : make (map [common.Address ]* StateObject ),
96102 stateObjectsDirty : make (map [common.Address ]struct {}),
97103 refund : new (big.Int ),
@@ -100,6 +106,30 @@ func (self *StateDB) Reset(root common.Hash) error {
100106 return nil
101107}
102108
109+ // openTrie creates a trie. It uses an existing trie if one is available
110+ // from the journal if available.
111+ func (self * StateDB ) openTrie (root common.Hash ) (* trie.SecureTrie , error ) {
112+ if self .trie != nil && self .trie .Hash () == root {
113+ return self .trie , nil
114+ }
115+ for i := len (self .pastTries ) - 1 ; i >= 0 ; i -- {
116+ if self .pastTries [i ].Hash () == root {
117+ tr := * self .pastTries [i ]
118+ return & tr , nil
119+ }
120+ }
121+ return trie .NewSecure (root , self .db )
122+ }
123+
124+ func (self * StateDB ) pushTrie (t * trie.SecureTrie ) {
125+ if len (self .pastTries ) >= maxJournalLength {
126+ copy (self .pastTries , self .pastTries [1 :])
127+ self .pastTries [len (self .pastTries )- 1 ] = t
128+ } else {
129+ self .pastTries = append (self .pastTries , t )
130+ }
131+ }
132+
103133func (self * StateDB ) StartRecord (thash , bhash common.Hash , ti int ) {
104134 self .thash = thash
105135 self .bhash = bhash
@@ -165,17 +195,28 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
165195func (self * StateDB ) GetCode (addr common.Address ) []byte {
166196 stateObject := self .GetStateObject (addr )
167197 if stateObject != nil {
168- return stateObject .Code (self .db )
198+ code := stateObject .Code (self .db )
199+ key := common .BytesToHash (stateObject .CodeHash ())
200+ self .codeSizeCache .Add (key , len (code ))
201+ return code
169202 }
170203 return nil
171204}
172205
173206func (self * StateDB ) GetCodeSize (addr common.Address ) int {
174207 stateObject := self .GetStateObject (addr )
175- if stateObject ! = nil {
176- return stateObject . CodeSize ( self . db )
208+ if stateObject = = nil {
209+ return 0
177210 }
178- return 0
211+ key := common .BytesToHash (stateObject .CodeHash ())
212+ if cached , ok := self .codeSizeCache .Get (key ); ok {
213+ return cached .(int )
214+ }
215+ size := len (stateObject .Code (self .db ))
216+ if stateObject .dbErr == nil {
217+ self .codeSizeCache .Add (key , size )
218+ }
219+ return size
179220}
180221
181222func (self * StateDB ) GetState (a common.Address , b common.Hash ) common.Hash {
@@ -269,13 +310,6 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
269310 return obj
270311 }
271312
272- // Use cached account data from the canon state if possible.
273- if data , ok := self .all [addr ]; ok {
274- obj := NewObject (addr , data , self .MarkStateObjectDirty )
275- self .SetStateObject (obj )
276- return obj
277- }
278-
279313 // Load the object from the database.
280314 enc := self .trie .Get (addr [:])
281315 if len (enc ) == 0 {
@@ -286,10 +320,6 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
286320 glog .Errorf ("can't decode object at %x: %v" , addr [:], err )
287321 return nil
288322 }
289- // Update the all cache. Content in DB always corresponds
290- // to the current head state so this is ok to do here.
291- // The object we just loaded has no storage trie and code yet.
292- self .all [addr ] = data
293323 // Insert into the live set.
294324 obj := NewObject (addr , data , self .MarkStateObjectDirty )
295325 self .SetStateObject (obj )
@@ -355,7 +385,8 @@ func (self *StateDB) Copy() *StateDB {
355385 state := & StateDB {
356386 db : self .db ,
357387 trie : self .trie ,
358- all : self .all ,
388+ pastTries : self .pastTries ,
389+ codeSizeCache : self .codeSizeCache ,
359390 stateObjects : make (map [common.Address ]* StateObject , len (self .stateObjectsDirty )),
360391 stateObjectsDirty : make (map [common.Address ]struct {}, len (self .stateObjectsDirty )),
361392 refund : new (big.Int ).Set (self .refund ),
@@ -375,11 +406,12 @@ func (self *StateDB) Copy() *StateDB {
375406}
376407
377408func (self * StateDB ) Set (state * StateDB ) {
409+ self .db = state .db
378410 self .trie = state .trie
411+ self .pastTries = state .pastTries
379412 self .stateObjects = state .stateObjects
380413 self .stateObjectsDirty = state .stateObjectsDirty
381- self .all = state .all
382-
414+ self .codeSizeCache = state .codeSizeCache
383415 self .refund = state .refund
384416 self .logs = state .logs
385417 self .logSize = state .logSize
@@ -444,20 +476,13 @@ func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
444476
445477func (s * StateDB ) commit (dbw trie.DatabaseWriter ) (root common.Hash , err error ) {
446478 s .refund = new (big.Int )
447- defer func () {
448- if err != nil {
449- // Committing failed, any updates to the canon state are invalid.
450- s .all = make (map [common.Address ]Account )
451- }
452- }()
453479
454480 // Commit objects to the trie.
455481 for addr , stateObject := range s .stateObjects {
456482 if stateObject .remove {
457483 // If the object has been removed, don't bother syncing it
458484 // and just mark it for deletion in the trie.
459485 s .DeleteStateObject (stateObject )
460- delete (s .all , addr )
461486 } else if _ , ok := s .stateObjectsDirty [addr ]; ok {
462487 // Write any contract code associated with the state object
463488 if stateObject .code != nil && stateObject .dirtyCode {
@@ -472,12 +497,15 @@ func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error)
472497 }
473498 // Update the object in the main account trie.
474499 s .UpdateStateObject (stateObject )
475- s .all [addr ] = stateObject .data
476500 }
477501 delete (s .stateObjectsDirty , addr )
478502 }
479503 // Write trie changes.
480- return s .trie .CommitTo (dbw )
504+ root , err = s .trie .CommitTo (dbw )
505+ if err == nil {
506+ s .pushTrie (s .trie )
507+ }
508+ return root , err
481509}
482510
483511func (self * StateDB ) Refunds () * big.Int {
0 commit comments