diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index 98c8d42a1a6c..52d4e5dfde50 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" @@ -434,6 +435,25 @@ func (dl *diskLayer) generateRange(root common.Hash, prefix []byte, kind string, } meter.Mark(1) } + // We use the snap data to build up a cache which can be used by the main + // account trie as a short-circuit when resolving hashes + if len(result.keys) > 0 { + var ( + cache = memorydb.New() + triedb = trie.NewDatabase(cache) + trie, _ = trie.New(common.Hash{}, triedb) + ) + for i, key := range result.keys { + trie.Update(key, result.vals[i]) + } + trie.Commit(nil) + + it := cache.NewIterator(nil, nil) + for it.Next() { + dl.triedb.Seed(common.BytesToHash(it.Key()), common.CopyBytes(it.Value())) + } + it.Release() + } tr := result.tr if tr == nil { tr, err = trie.New(root, dl.triedb) diff --git a/trie/database.go b/trie/database.go index b18665770e46..0c57e3565e6e 100644 --- a/trie/database.go +++ b/trie/database.go @@ -893,3 +893,15 @@ func (db *Database) SaveCachePeriodically(dir string, interval time.Duration, st } } } + +// Seed can be used to inject a single trie node into the clean cache. Its main +// purpose is to allow operating on trie nodes that are known to be on disk, but +// if they are available in RAM too (generated from snapshot), instead of loading +// again from persistent storage (expensive), rather inject into the cache to be +// loaded from there (cheap). +func (db *Database) Seed(key common.Hash, val []byte) { + db.lock.RLock() + defer db.lock.RUnlock() + + db.cleans.Set(key[:], val) +}