@@ -28,23 +28,32 @@ import (
28
28
"github.com/ethereum/go-ethereum/logger/glog"
29
29
"github.com/ethereum/go-ethereum/rlp"
30
30
"github.com/ethereum/go-ethereum/trie"
31
+ lru "github.com/hashicorp/golang-lru"
31
32
)
32
33
33
34
// The starting nonce determines the default nonce when new accounts are being
34
35
// created.
35
36
var StartingNonce uint64
36
37
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
+
37
47
// StateDBs within the ethereum protocol are used to store anything
38
48
// within the merkle trie. StateDBs take care of caching and storing
39
49
// nested states. It's the general query interface to retrieve:
40
50
// * Contracts
41
51
// * Accounts
42
52
type 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
48
57
49
58
// This map holds 'live' objects, which will get modified while processing a state transition.
50
59
stateObjects map [common.Address ]* StateObject
@@ -65,10 +74,11 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
65
74
if err != nil {
66
75
return nil , err
67
76
}
77
+ csc , _ := lru .New (codeSizeCacheSize )
68
78
return & StateDB {
69
79
db : db ,
70
80
trie : tr ,
71
- all : make ( map [common. Address ] Account ) ,
81
+ codeSizeCache : csc ,
72
82
stateObjects : make (map [common.Address ]* StateObject ),
73
83
stateObjectsDirty : make (map [common.Address ]struct {}),
74
84
refund : new (big.Int ),
@@ -79,19 +89,15 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
79
89
// Reset clears out all emphemeral state objects from the state db, but keeps
80
90
// the underlying state trie to avoid reloading data for the next operations.
81
91
func (self * StateDB ) Reset (root common.Hash ) error {
82
- tr , err := trie . NewSecure (root , self . db )
92
+ tr , err := self . openTrie (root )
83
93
if err != nil {
84
94
return err
85
95
}
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
- }
91
96
* self = StateDB {
92
97
db : self .db ,
93
98
trie : tr ,
94
- all : all ,
99
+ pastTries : self .pastTries ,
100
+ codeSizeCache : self .codeSizeCache ,
95
101
stateObjects : make (map [common.Address ]* StateObject ),
96
102
stateObjectsDirty : make (map [common.Address ]struct {}),
97
103
refund : new (big.Int ),
@@ -100,6 +106,30 @@ func (self *StateDB) Reset(root common.Hash) error {
100
106
return nil
101
107
}
102
108
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
+
103
133
func (self * StateDB ) StartRecord (thash , bhash common.Hash , ti int ) {
104
134
self .thash = thash
105
135
self .bhash = bhash
@@ -165,17 +195,28 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
165
195
func (self * StateDB ) GetCode (addr common.Address ) []byte {
166
196
stateObject := self .GetStateObject (addr )
167
197
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
169
202
}
170
203
return nil
171
204
}
172
205
173
206
func (self * StateDB ) GetCodeSize (addr common.Address ) int {
174
207
stateObject := self .GetStateObject (addr )
175
- if stateObject ! = nil {
176
- return stateObject . CodeSize ( self . db )
208
+ if stateObject = = nil {
209
+ return 0
177
210
}
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
179
220
}
180
221
181
222
func (self * StateDB ) GetState (a common.Address , b common.Hash ) common.Hash {
@@ -269,13 +310,6 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
269
310
return obj
270
311
}
271
312
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
-
279
313
// Load the object from the database.
280
314
enc := self .trie .Get (addr [:])
281
315
if len (enc ) == 0 {
@@ -286,10 +320,6 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
286
320
glog .Errorf ("can't decode object at %x: %v" , addr [:], err )
287
321
return nil
288
322
}
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
293
323
// Insert into the live set.
294
324
obj := NewObject (addr , data , self .MarkStateObjectDirty )
295
325
self .SetStateObject (obj )
@@ -355,7 +385,8 @@ func (self *StateDB) Copy() *StateDB {
355
385
state := & StateDB {
356
386
db : self .db ,
357
387
trie : self .trie ,
358
- all : self .all ,
388
+ pastTries : self .pastTries ,
389
+ codeSizeCache : self .codeSizeCache ,
359
390
stateObjects : make (map [common.Address ]* StateObject , len (self .stateObjectsDirty )),
360
391
stateObjectsDirty : make (map [common.Address ]struct {}, len (self .stateObjectsDirty )),
361
392
refund : new (big.Int ).Set (self .refund ),
@@ -375,11 +406,12 @@ func (self *StateDB) Copy() *StateDB {
375
406
}
376
407
377
408
func (self * StateDB ) Set (state * StateDB ) {
409
+ self .db = state .db
378
410
self .trie = state .trie
411
+ self .pastTries = state .pastTries
379
412
self .stateObjects = state .stateObjects
380
413
self .stateObjectsDirty = state .stateObjectsDirty
381
- self .all = state .all
382
-
414
+ self .codeSizeCache = state .codeSizeCache
383
415
self .refund = state .refund
384
416
self .logs = state .logs
385
417
self .logSize = state .logSize
@@ -444,20 +476,13 @@ func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
444
476
445
477
func (s * StateDB ) commit (dbw trie.DatabaseWriter ) (root common.Hash , err error ) {
446
478
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
- }()
453
479
454
480
// Commit objects to the trie.
455
481
for addr , stateObject := range s .stateObjects {
456
482
if stateObject .remove {
457
483
// If the object has been removed, don't bother syncing it
458
484
// and just mark it for deletion in the trie.
459
485
s .DeleteStateObject (stateObject )
460
- delete (s .all , addr )
461
486
} else if _ , ok := s .stateObjectsDirty [addr ]; ok {
462
487
// Write any contract code associated with the state object
463
488
if stateObject .code != nil && stateObject .dirtyCode {
@@ -472,12 +497,15 @@ func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error)
472
497
}
473
498
// Update the object in the main account trie.
474
499
s .UpdateStateObject (stateObject )
475
- s .all [addr ] = stateObject .data
476
500
}
477
501
delete (s .stateObjectsDirty , addr )
478
502
}
479
503
// 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
481
509
}
482
510
483
511
func (self * StateDB ) Refunds () * big.Int {
0 commit comments