@@ -21,6 +21,7 @@ import (
2121 "fmt"
2222 "io"
2323 "maps"
24+ "sync"
2425 "time"
2526
2627 "github.com/ethereum/go-ethereum/common"
@@ -33,6 +34,14 @@ import (
3334 "github.com/holiman/uint256"
3435)
3536
37+ // hasherPool holds a pool of hashers used by state objects during concurrent
38+ // trie updates.
39+ var hasherPool = sync.Pool {
40+ New : func () interface {} {
41+ return crypto .NewKeccakState ()
42+ },
43+ }
44+
3645type Storage map [common.Hash ]common.Hash
3746
3847func (s Storage ) Copy () Storage {
@@ -118,27 +127,39 @@ func (s *stateObject) touch() {
118127 }
119128}
120129
121- // getTrie returns the associated storage trie. The trie will be opened
122- // if it's not loaded previously. An error will be returned if trie can't
123- // be loaded.
130+ // getTrie returns the associated storage trie. The trie will be opened if it'
131+ // not loaded previously. An error will be returned if trie can't be loaded.
132+ //
133+ // If a new trie is opened, it will be cached within the state object to allow
134+ // subsequent reads to expand the same trie instead of reloading from disk.
124135func (s * stateObject ) getTrie () (Trie , error ) {
125136 if s .trie == nil {
126- // Try fetching from prefetcher first
127- if s .data .Root != types .EmptyRootHash && s .db .prefetcher != nil {
128- // When the miner is creating the pending state, there is no prefetcher
129- s .trie = s .db .prefetcher .trie (s .addrHash , s .data .Root )
130- }
131- if s .trie == nil {
132- tr , err := s .db .db .OpenStorageTrie (s .db .originalRoot , s .address , s .data .Root , s .db .trie )
133- if err != nil {
134- return nil , err
135- }
136- s .trie = tr
137+ tr , err := s .db .db .OpenStorageTrie (s .db .originalRoot , s .address , s .data .Root , s .db .trie )
138+ if err != nil {
139+ return nil , err
137140 }
141+ s .trie = tr
138142 }
139143 return s .trie , nil
140144}
141145
146+ // getPrefetchedTrie returns the associated trie, as populated by the prefetcher
147+ // if it's available.
148+ //
149+ // Note, opposed to getTrie, this method will *NOT* blindly cache the resulting
150+ // trie in the state object. The caller might want to do that, but it's cleaner
151+ // to break the hidden interdependency between retrieving tries from the db or
152+ // from the prefetcher.
153+ func (s * stateObject ) getPrefetchedTrie () (Trie , error ) {
154+ // If there's nothing to meaningfully return, let the user figure it out by
155+ // pulling the trie from disk.
156+ if s .data .Root == types .EmptyRootHash || s .db .prefetcher == nil {
157+ return nil , nil
158+ }
159+ // Attempt to retrieve the trie from the pretecher
160+ return s .db .prefetcher .trie (s .addrHash , s .data .Root )
161+ }
162+
142163// GetState retrieves a value from the account storage trie.
143164func (s * stateObject ) GetState (key common.Hash ) common.Hash {
144165 value , _ := s .getState (key )
@@ -248,7 +269,7 @@ func (s *stateObject) setState(key common.Hash, value common.Hash, origin common
248269
249270// finalise moves all dirty storage slots into the pending area to be hashed or
250271// committed later. It is invoked at the end of every transaction.
251- func (s * stateObject ) finalise (prefetch bool ) {
272+ func (s * stateObject ) finalise () {
252273 slotsToPrefetch := make ([][]byte , 0 , len (s .dirtyStorage ))
253274 for key , value := range s .dirtyStorage {
254275 // If the slot is different from its original value, move it into the
@@ -263,8 +284,10 @@ func (s *stateObject) finalise(prefetch bool) {
263284 delete (s .pendingStorage , key )
264285 }
265286 }
266- if s .db .prefetcher != nil && prefetch && len (slotsToPrefetch ) > 0 && s .data .Root != types .EmptyRootHash {
267- s .db .prefetcher .prefetch (s .addrHash , s .data .Root , s .address , slotsToPrefetch )
287+ if s .db .prefetcher != nil && len (slotsToPrefetch ) > 0 && s .data .Root != types .EmptyRootHash {
288+ if err := s .db .prefetcher .prefetch (s .addrHash , s .data .Root , s .address , slotsToPrefetch ); err != nil {
289+ log .Error ("Failed to prefetch slots" , "addr" , s .address , "slots" , len (slotsToPrefetch ), "err" , err )
290+ }
268291 }
269292 if len (s .dirtyStorage ) > 0 {
270293 s .dirtyStorage = make (Storage )
@@ -283,25 +306,43 @@ func (s *stateObject) finalise(prefetch bool) {
283306// storage change at all.
284307func (s * stateObject ) updateTrie () (Trie , error ) {
285308 // Make sure all dirty slots are finalized into the pending storage area
286- s .finalise (false )
309+ s .finalise ()
287310
288311 // Short circuit if nothing changed, don't bother with hashing anything
289312 if len (s .pendingStorage ) == 0 {
290313 return s .trie , nil
291314 }
315+ // Retrieve a pretecher populated trie, or fall back to the database
316+ tr , err := s .getPrefetchedTrie ()
317+ switch {
318+ case err != nil :
319+ // Fetcher retrieval failed, something's very wrong, abort
320+ s .db .setError (err )
321+ return nil , err
322+
323+ case tr == nil :
324+ // Fetcher not running or empty trie, fallback to the database trie
325+ tr , err = s .getTrie ()
326+ if err != nil {
327+ s .db .setError (err )
328+ return nil , err
329+ }
330+
331+ default :
332+ // Prefetcher returned a live trie, swap it out for the current one
333+ s .trie = tr
334+ }
292335 // The snapshot storage map for the object
293336 var (
294337 storage map [common.Hash ][]byte
295338 origin map [common.Hash ][]byte
296339 )
297- tr , err := s .getTrie ()
298- if err != nil {
299- s .db .setError (err )
300- return nil , err
301- }
302340 // Insert all the pending storage updates into the trie
303341 usedStorage := make ([][]byte , 0 , len (s .pendingStorage ))
304342
343+ hasher := hasherPool .Get ().(crypto.KeccakState )
344+ defer hasherPool .Put (hasher )
345+
305346 // Perform trie updates before deletions. This prevents resolution of unnecessary trie nodes
306347 // in circumstances similar to the following:
307348 //
@@ -330,26 +371,30 @@ func (s *stateObject) updateTrie() (Trie, error) {
330371 s .db .setError (err )
331372 return nil , err
332373 }
333- s .db .StorageUpdated += 1
374+ s .db .StorageUpdated . Add ( 1 )
334375 } else {
335376 deletions = append (deletions , key )
336377 }
337378 // Cache the mutated storage slots until commit
338379 if storage == nil {
380+ s .db .storagesLock .Lock ()
339381 if storage = s .db .storages [s .addrHash ]; storage == nil {
340382 storage = make (map [common.Hash ][]byte )
341383 s .db .storages [s .addrHash ] = storage
342384 }
385+ s .db .storagesLock .Unlock ()
343386 }
344- khash := crypto .HashData (s . db . hasher , key [:])
387+ khash := crypto .HashData (hasher , key [:])
345388 storage [khash ] = encoded // encoded will be nil if it's deleted
346389
347390 // Cache the original value of mutated storage slots
348391 if origin == nil {
392+ s .db .storagesLock .Lock ()
349393 if origin = s .db .storagesOrigin [s .address ]; origin == nil {
350394 origin = make (map [common.Hash ][]byte )
351395 s .db .storagesOrigin [s .address ] = origin
352396 }
397+ s .db .storagesLock .Unlock ()
353398 }
354399 // Track the original value of slot only if it's mutated first time
355400 if _ , ok := origin [khash ]; ! ok {
@@ -369,7 +414,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
369414 s .db .setError (err )
370415 return nil , err
371416 }
372- s .db .StorageDeleted += 1
417+ s .db .StorageDeleted . Add ( 1 )
373418 }
374419 // If no slots were touched, issue a warning as we shouldn't have done all
375420 // the above work in the first place
0 commit comments