@@ -59,14 +59,15 @@ func (s Storage) Copy() Storage {
5959// stateObject represents an Ethereum account which is being modified.
6060//
6161// The usage pattern is as follows:
62- // First you need to obtain a state object.
63- // Account values can be accessed and modified through the object.
64- // Finally, call commitTrie to write the modified storage trie into a database .
62+ // - First you need to obtain a state object.
63+ // - Account values as well as storages can be accessed and modified through the object.
64+ // - Finally, call commit to return the changes of storage trie and update account data .
6565type stateObject struct {
6666 db * StateDB
67- address common.Address // address of ethereum account
68- addrHash common.Hash // hash of ethereum address of the account
69- data types.StateAccount // Account data with all mutations applied in the scope of block
67+ address common.Address // address of ethereum account
68+ addrHash common.Hash // hash of ethereum address of the account
69+ origin * types.StateAccount // Account original data without any change applied, nil means it was not existent
70+ data types.StateAccount // Account data with all mutations applied in the scope of block
7071
7172 // DB error.
7273 // State objects are used by the consensus core and VM which are
@@ -79,9 +80,9 @@ type stateObject struct {
7980 trie Trie // storage trie, which becomes non-nil on first access
8081 code Code // contract bytecode, which gets set when code is loaded
8182
82- originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
83+ originStorage Storage // Storage cache of original entries to dedup rewrites
8384 pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
84- dirtyStorage Storage // Storage entries that need to be flushed to disk
85+ dirtyStorage Storage // Storage entries that need to be flushed to disk, reset for every transaction
8586
8687 // Cache flags.
8788 dirtyCode bool // true if the code was updated
@@ -105,21 +106,17 @@ func (s *stateObject) empty() bool {
105106}
106107
107108// newObject creates a state object.
108- func newObject (db * StateDB , address common.Address , data types.StateAccount ) * stateObject {
109- if data .Balance == nil {
110- data .Balance = new (big.Int )
111- }
112- if data .CodeHash == nil {
113- data .CodeHash = types .EmptyCodeHash .Bytes ()
114- }
115- if data .Root == (common.Hash {}) {
116- data .Root = types .EmptyRootHash
109+ func newObject (db * StateDB , address common.Address , acct * types.StateAccount ) * stateObject {
110+ origin := acct
111+ if acct == nil {
112+ acct = types .NewEmptyStateAccount ()
117113 }
118114 return & stateObject {
119115 db : db ,
120116 address : address ,
121117 addrHash : crypto .Keccak256Hash (address [:]),
122- data : data ,
118+ origin : origin ,
119+ data : * acct ,
123120 originStorage : make (Storage ),
124121 pendingStorage : make (Storage ),
125122 dirtyStorage : make (Storage ),
@@ -266,34 +263,74 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
266263 }
267264 // Track the amount of time wasted on updating the storage trie
268265 defer func (start time.Time ) { s .db .StorageUpdates += time .Since (start ) }(time .Now ())
266+ // The snapshot storage map for the object
267+ var (
268+ storage map [common.Hash ][]byte
269+ origin map [common.Hash ][]byte
270+ hasher = s .db .hasher
271+ )
269272 tr , err := s .getTrie (db )
270273 if err != nil {
271274 s .setError (err )
272275 return nil , err
273276 }
274277 // Insert all the pending updates into the trie
278+ usedStorage := make ([][]byte , 0 , len (s .pendingStorage ))
275279 for key , value := range s .pendingStorage {
276280 // Skip noop changes, persist actual changes
277281 if value == s .originStorage [key ] {
278282 continue
279283 }
284+ prev := s .originStorage [key ]
280285 s .originStorage [key ] = value
281286
282- if (value == common.Hash {}) {
287+ // rlp-encoded value to be used by the snapshot
288+ var snapshotVal []byte
289+ if value == (common.Hash {}) {
283290 if err := tr .TryDelete (key [:]); err != nil {
284291 s .setError (err )
285292 return nil , err
286293 }
287294 s .db .StorageDeleted += 1
288295 } else {
296+ trimmedVal := common .TrimLeftZeroes (value [:])
289297 // Encoding []byte cannot fail, ok to ignore the error.
290- v , _ := rlp .EncodeToBytes (common . TrimLeftZeroes ( value [:]) )
291- if err := tr .TryUpdate (key [:], v ); err != nil {
298+ snapshotVal , _ := rlp .EncodeToBytes (trimmedVal )
299+ if err := tr .TryUpdate (key [:], snapshotVal ); err != nil {
292300 s .setError (err )
293301 return nil , err
294302 }
295303 s .db .StorageUpdated += 1
296304 }
305+ // Cache the mutated storage slots until commit
306+ if storage == nil {
307+ if storage = s .db .storages [s .addrHash ]; storage == nil {
308+ storage = make (map [common.Hash ][]byte )
309+ s .db .storages [s .addrHash ] = storage
310+ }
311+ }
312+ khash := crypto .HashData (hasher , key [:])
313+ storage [khash ] = snapshotVal // snapshotVal will be nil if it's deleted
314+
315+ // Cache the original value of mutated storage slots
316+ if origin == nil {
317+ if origin = s .db .storagesOrigin [s .addrHash ]; origin == nil {
318+ origin = make (map [common.Hash ][]byte )
319+ s .db .storagesOrigin [s .addrHash ] = origin
320+ }
321+ }
322+ // Track the original value of slot only if it's mutated first time
323+ if _ , ok := origin [khash ]; ! ok {
324+ if prev == (common.Hash {}) {
325+ origin [khash ] = nil // nil if it was not present previously
326+ } else {
327+ // Encoding []byte cannot fail, ok to ignore the error.
328+ b , _ := rlp .EncodeToBytes (common .TrimLeftZeroes (prev [:]))
329+ origin [khash ] = b
330+ }
331+ }
332+ // Cache the items for preloading
333+ usedStorage = append (usedStorage , common .CopyBytes (key [:])) // Copy needed for closure
297334 }
298335 if len (s .pendingStorage ) > 0 {
299336 s .pendingStorage = make (Storage )
@@ -318,9 +355,8 @@ func (s *stateObject) updateRoot(db Database) {
318355 s .data .Root = tr .Hash ()
319356}
320357
321- // CommitTrie the storage trie of the object to dwb.
322- // This updates the trie root.
323- func (s * stateObject ) commitTrie (db Database ) (* trie.NodeSet , error ) {
358+ // commit returns the changes made in storage trie and updates the account data.
359+ func (s * stateObject ) commit (db Database ) (* trie.NodeSet , error ) {
324360 // If nothing changed, don't bother with hashing anything
325361 tr , err := s .updateTrie (db )
326362 if err != nil {
@@ -331,6 +367,7 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
331367 }
332368 // If nothing changed, don't bother with hashing anything
333369 if tr == nil {
370+ s .origin = s .data .Copy ()
334371 return nil , nil
335372 }
336373 // Track the amount of time wasted on committing the storage trie
@@ -339,6 +376,9 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
339376 if err == nil {
340377 s .data .Root = root
341378 }
379+
380+ // Update original account data after commit
381+ s .origin = s .data .Copy ()
342382 return nodes , err
343383}
344384
@@ -381,18 +421,24 @@ func (s *stateObject) setBalance(amount *big.Int) {
381421}
382422
383423func (s * stateObject ) deepCopy (db * StateDB ) * stateObject {
384- stateObject := newObject (db , s .address , s .data )
424+ obj := & stateObject {
425+ db : db ,
426+ address : s .address ,
427+ addrHash : s .addrHash ,
428+ origin : s .origin ,
429+ data : s .data ,
430+ }
385431 if s .trie != nil {
386- stateObject .trie = db .db .CopyTrie (s .trie )
387- }
388- stateObject .code = s .code
389- stateObject .dirtyStorage = s .dirtyStorage .Copy ()
390- stateObject .originStorage = s .originStorage .Copy ()
391- stateObject .pendingStorage = s .pendingStorage .Copy ()
392- stateObject .selfDestructed = s .selfDestructed
393- stateObject .dirtyCode = s .dirtyCode
394- stateObject .deleted = s .deleted
395- return stateObject
432+ obj .trie = db .db .CopyTrie (s .trie )
433+ }
434+ obj .code = s .code
435+ obj .dirtyStorage = s .dirtyStorage .Copy ()
436+ obj .originStorage = s .originStorage .Copy ()
437+ obj .pendingStorage = s .pendingStorage .Copy ()
438+ obj .selfDestructed = s .selfDestructed
439+ obj .dirtyCode = s .dirtyCode
440+ obj .deleted = s .deleted
441+ return obj
396442}
397443
398444//
0 commit comments