@ -298,6 +298,18 @@ func (s *stateObject) updateTrie() (Trie, error) {
}
// Insert all the pending storage updates into the trie
usedStorage := make ( [ ] [ ] byte , 0 , len ( s . pendingStorage ) )
// Perform trie updates before deletions. This prevents resolution of unnecessary trie nodes
// in circumstances similar to the following:
//
// Consider nodes `A` and `B` who share the same full node parent `P` and have no other siblings.
// During the execution of a block:
// - `A` is deleted,
// - `C` is created, and also shares the parent `P`.
// If the deletion is handled first, then `P` would be left with only one child, thus collapsed
// into a shortnode. This requires `B` to be resolved from disk.
// Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved.
var deletions [ ] common . Hash
for key , value := range s . pendingStorage {
// Skip noop changes, persist actual changes
if value == s . originStorage [ key ] {
@ -307,13 +319,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
s . originStorage [ key ] = value
var encoded [ ] byte // rlp-encoded value to be used by the snapshot
if ( value == common . Hash { } ) {
if err := tr . DeleteStorage ( s . address , key [ : ] ) ; err != nil {
s . db . setError ( err )
return nil , err
}
s . db . StorageDeleted += 1
} else {
if ( value != common . Hash { } ) {
// Encoding []byte cannot fail, ok to ignore the error.
trimmed := common . TrimLeftZeroes ( value [ : ] )
encoded , _ = rlp . EncodeToBytes ( trimmed )
@ -322,6 +328,8 @@ func (s *stateObject) updateTrie() (Trie, error) {
return nil , err
}
s . db . StorageUpdated += 1
} else {
deletions = append ( deletions , key )
}
// Cache the mutated storage slots until commit
if storage == nil {
@ -353,6 +361,13 @@ func (s *stateObject) updateTrie() (Trie, error) {
// Cache the items for preloading
usedStorage = append ( usedStorage , common . CopyBytes ( key [ : ] ) ) // Copy needed for closure
}
for _ , key := range deletions {
if err := tr . DeleteStorage ( s . address , key [ : ] ) ; err != nil {
s . db . setError ( err )
return nil , err
}
s . db . StorageDeleted += 1
}
if s . db . prefetcher != nil {
s . db . prefetcher . used ( s . addrHash , s . data . Root , usedStorage )
}