@ -148,7 +148,10 @@ func (s *stateObject) touch() {
}
}
func ( s * stateObject ) getTrie ( db Database ) Trie {
// getTrie returns the associated storage trie. The trie will be opened
// if it's not loaded previously. An error will be returned if trie can't
// be loaded.
func ( s * stateObject ) getTrie ( db Database ) ( Trie , error ) {
if s . trie == nil {
// Try fetching from prefetcher first
// We don't prefetch empty tries
@ -158,15 +161,14 @@ func (s *stateObject) getTrie(db Database) Trie {
s . trie = s . db . prefetcher . trie ( s . addrHash , s . data . Root )
}
if s . trie == nil {
var err error
s . trie , err = db . OpenStorageTrie ( s . db . originalRoot , s . addrHash , s . data . Root )
tr , err := db . OpenStorageTrie ( s . db . originalRoot , s . addrHash , s . data . Root )
if err != nil {
s . trie , _ = db . OpenStorageTrie ( s . db . originalRoot , s . addrHash , common . Hash { } )
s . setError ( fmt . Errorf ( "can't create storage trie: %v" , err ) )
return nil , err
}
s . trie = tr
}
}
return s . trie
return s . trie , nil
}
// GetState retrieves a value from the account storage trie.
@ -221,7 +223,12 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
// If the snapshot is unavailable or reading from it fails, load from the database.
if s . db . snap == nil || err != nil {
start := time . Now ( )
enc , err = s . getTrie ( db ) . TryGet ( key . Bytes ( ) )
tr , err := s . getTrie ( db )
if err != nil {
s . setError ( err )
return common . Hash { }
}
enc , err = tr . TryGet ( key . Bytes ( ) )
if metrics . EnabledExpensive {
s . db . StorageReads += time . Since ( start )
}
@ -304,23 +311,29 @@ func (s *stateObject) finalise(prefetch bool) {
}
// updateTrie writes cached storage modifications into the object's storage trie.
// It will return nil if the trie has not been loaded and no changes have been made
func ( s * stateObject ) updateTrie ( db Database ) Trie {
// It will return nil if the trie has not been loaded and no changes have been
// made. An error will be returned if the trie can't be loaded/updated correctly.
func ( s * stateObject ) updateTrie ( db Database ) ( Trie , error ) {
// Make sure all dirty slots are finalized into the pending storage area
s . finalise ( false ) // Don't prefetch anymore, pull directly if need be
if len ( s . pendingStorage ) == 0 {
return s . trie
return s . trie , nil
}
// Track the amount of time wasted on updating the storage trie
if metrics . EnabledExpensive {
defer func ( start time . Time ) { s . db . StorageUpdates += time . Since ( start ) } ( time . Now ( ) )
}
// The snapshot storage map for the object
var storage map [ common . Hash ] [ ] byte
var (
storage map [ common . Hash ] [ ] byte
hasher = s . db . hasher
)
tr , err := s . getTrie ( db )
if err != nil {
s . setError ( err )
return nil , err
}
// Insert all the pending updates into the trie
tr := s . getTrie ( db )
hasher := s . db . hasher
usedStorage := make ( [ ] [ ] byte , 0 , len ( s . pendingStorage ) )
for key , value := range s . pendingStorage {
// Skip noop changes, persist actual changes
@ -331,12 +344,18 @@ func (s *stateObject) updateTrie(db Database) Trie {
var v [ ] byte
if ( value == common . Hash { } ) {
s . setError ( tr . TryDelete ( key [ : ] ) )
if err := tr . TryDelete ( key [ : ] ) ; err != nil {
s . setError ( err )
return nil , err
}
s . db . StorageDeleted += 1
} else {
// Encoding []byte cannot fail, ok to ignore the error.
v , _ = rlp . EncodeToBytes ( common . TrimLeftZeroes ( value [ : ] ) )
s . setError ( tr . TryUpdate ( key [ : ] , v ) )
if err := tr . TryUpdate ( key [ : ] , v ) ; err != nil {
s . setError ( err )
return nil , err
}
s . db . StorageUpdated += 1
}
// If state snapshotting is active, cache the data til commit
@ -358,37 +377,47 @@ func (s *stateObject) updateTrie(db Database) Trie {
if len ( s . pendingStorage ) > 0 {
s . pendingStorage = make ( Storage )
}
return tr
return tr , nil
}
// UpdateRoot sets the trie root to the current root hash of
// UpdateRoot sets the trie root to the current root hash of. An error
// will be returned if trie root hash is not computed correctly.
func ( s * stateObject ) updateRoot ( db Database ) {
tr , err := s . updateTrie ( db )
if err != nil {
s . setError ( fmt . Errorf ( "updateRoot (%x) error: %w" , s . address , err ) )
return
}
// If nothing changed, don't bother with hashing anything
if s . updateTrie ( db ) == nil {
if tr == nil {
return
}
// Track the amount of time wasted on hashing the storage trie
if metrics . EnabledExpensive {
defer func ( start time . Time ) { s . db . StorageHashes += time . Since ( start ) } ( time . Now ( ) )
}
s . data . Root = s . trie . Hash ( )
s . data . Root = tr . Hash ( )
}
// commitTrie submits the storage changes into the storage trie and re-computes
// the root. Besides, all trie changes will be collected in a nodeset and returned.
func ( s * stateObject ) commitTrie ( db Database ) ( * trie . NodeSet , error ) {
// If nothing changed, don't bother with hashing anything
if s . updateTrie ( db ) = = nil {
return nil , nil
tr , err := s . updateTrie ( db )
if err ! = nil {
return nil , err
}
if s . dbErr != nil {
return nil , s . dbErr
}
// If nothing changed, don't bother with committing anything
if tr == nil {
return nil , nil
}
// Track the amount of time wasted on committing the storage trie
if metrics . EnabledExpensive {
defer func ( start time . Time ) { s . db . StorageCommits += time . Since ( start ) } ( time . Now ( ) )
}
root , nodes , err := s . trie . Commit ( false )
root , nodes , err := tr . Commit ( false )
if err == nil {
s . data . Root = root
}