Skip to content

Commit f896a96

Browse files
rjl493456442cp-wjhan
authored andcommitted
core, trie, eth, cmd: rework preimage store (ethereum#25287)
* core, trie, eth, cmd: rework preimage store * trie: address comment
1 parent a5d9c2f commit f896a96

File tree

6 files changed

+127
-84
lines changed

6 files changed

+127
-84
lines changed

cmd/evm/internal/t8ntool/execution.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
268268
}
269269

270270
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
271-
sdb := state.NewDatabase(db)
271+
sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
272272
statedb, _ := state.New(common.Hash{}, sdb, nil)
273273
for addr, a := range accounts {
274274
statedb.SetCode(addr, a.Code)

core/state/state_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/ethereum/go-ethereum/core/rawdb"
2626
"github.com/ethereum/go-ethereum/crypto"
2727
"github.com/ethereum/go-ethereum/ethdb"
28+
"github.com/ethereum/go-ethereum/trie"
2829
)
2930

3031
type stateTest struct {
@@ -40,7 +41,7 @@ func newStateTest() *stateTest {
4041

4142
func TestDump(t *testing.T) {
4243
db := rawdb.NewMemoryDatabase()
43-
sdb, _ := New(common.Hash{}, NewDatabaseWithConfig(db, nil), nil)
44+
sdb, _ := New(common.Hash{}, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}), nil)
4445
s := &stateTest{db: db, state: sdb}
4546

4647
// generate a few entries

eth/api_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/ethereum/go-ethereum/core/rawdb"
3030
"github.com/ethereum/go-ethereum/core/state"
3131
"github.com/ethereum/go-ethereum/crypto"
32+
"github.com/ethereum/go-ethereum/trie"
3233
)
3334

3435
var dumper = spew.ConfigState{Indent: " "}
@@ -66,7 +67,7 @@ func TestAccountRange(t *testing.T) {
6667
t.Parallel()
6768

6869
var (
69-
statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), nil)
70+
statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true})
7071
state, _ = state.New(common.Hash{}, statedb, nil)
7172
addrs = [AccountRangeMaxResults * 2]common.Address{}
7273
m = map[common.Address]bool{}

trie/database.go

Lines changed: 16 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ type Database struct {
7474
oldest common.Hash // Oldest tracked node, flush-list head
7575
newest common.Hash // Newest tracked node, flush-list tail
7676

77-
preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
78-
7977
gctime time.Duration // Time spent on garbage collection since last commit
8078
gcnodes uint64 // Nodes garbage collected since last commit
8179
gcsize common.StorageSize // Data storage garbage collected since last commit
@@ -84,9 +82,9 @@ type Database struct {
8482
flushnodes uint64 // Nodes flushed since last commit
8583
flushsize common.StorageSize // Data storage flushed since last commit
8684

87-
dirtiesSize common.StorageSize // Storage size of the dirty node cache (exc. metadata)
88-
childrenSize common.StorageSize // Storage size of the external children tracking
89-
preimagesSize common.StorageSize // Storage size of the preimages cache
85+
dirtiesSize common.StorageSize // Storage size of the dirty node cache (exc. metadata)
86+
childrenSize common.StorageSize // Storage size of the external children tracking
87+
preimages *preimageStore // The store for caching preimages
9088

9189
lock sync.RWMutex
9290
}
@@ -287,15 +285,17 @@ func NewDatabaseWithConfig(diskdb ethdb.KeyValueStore, config *Config) *Database
287285
cleans = fastcache.LoadFromFileOrNew(config.Journal, config.Cache*1024*1024)
288286
}
289287
}
288+
var preimage *preimageStore
289+
if config != nil && config.Preimages {
290+
preimage = newPreimageStore(diskdb)
291+
}
290292
db := &Database{
291293
diskdb: diskdb,
292294
cleans: cleans,
293295
dirties: map[common.Hash]*cachedNode{{}: {
294296
children: make(map[common.Hash]uint16),
295297
}},
296-
}
297-
if config == nil || config.Preimages { // TODO(karalabe): Flip to default off in the future
298-
db.preimages = make(map[common.Hash][]byte)
298+
preimages: preimage,
299299
}
300300
return db
301301
}
@@ -341,24 +341,6 @@ func (db *Database) insert(hash common.Hash, size int, node node) {
341341
db.dirtiesSize += common.StorageSize(common.HashLength + entry.size)
342342
}
343343

344-
// insertPreimage writes a new trie node pre-image to the memory database if it's
345-
// yet unknown. The method will NOT make a copy of the slice,
346-
// only use if the preimage will NOT be changed later on.
347-
//
348-
// Note, this method assumes that the database's lock is held!
349-
func (db *Database) insertPreimage(hash common.Hash, preimage []byte) {
350-
// Short circuit if preimage collection is disabled
351-
if db.preimages == nil {
352-
return
353-
}
354-
// Track the preimage if a yet unknown one
355-
if _, ok := db.preimages[hash]; ok {
356-
return
357-
}
358-
db.preimages[hash] = preimage
359-
db.preimagesSize += common.StorageSize(common.HashLength + len(preimage))
360-
}
361-
362344
// node retrieves a cached trie node from memory, or returns nil if none can be
363345
// found in the memory cache.
364346
func (db *Database) node(hash common.Hash) node {
@@ -435,24 +417,6 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
435417
return nil, errors.New("not found")
436418
}
437419

438-
// preimage retrieves a cached trie node pre-image from memory. If it cannot be
439-
// found cached, the method queries the persistent database for the content.
440-
func (db *Database) preimage(hash common.Hash) []byte {
441-
// Short circuit if preimage collection is disabled
442-
if db.preimages == nil {
443-
return nil
444-
}
445-
// Retrieve the node from cache if available
446-
db.lock.RLock()
447-
preimage := db.preimages[hash]
448-
db.lock.RUnlock()
449-
450-
if preimage != nil {
451-
return preimage
452-
}
453-
return rawdb.ReadPreimage(db.diskdb, hash)
454-
}
455-
456420
// Nodes retrieves the hashes of all the nodes cached within the memory database.
457421
// This method is extremely expensive and should only be used to validate internal
458422
// states in test code.
@@ -597,19 +561,8 @@ func (db *Database) Cap(limit common.StorageSize) error {
597561

598562
// If the preimage cache got large enough, push to disk. If it's still small
599563
// leave for later to deduplicate writes.
600-
flushPreimages := db.preimagesSize > 4*1024*1024
601-
if flushPreimages {
602-
if db.preimages == nil {
603-
log.Error("Attempted to write preimages whilst disabled")
604-
} else {
605-
rawdb.WritePreimages(batch, db.preimages)
606-
if batch.ValueSize() > ethdb.IdealBatchSize {
607-
if err := batch.Write(); err != nil {
608-
return err
609-
}
610-
batch.Reset()
611-
}
612-
}
564+
if db.preimages != nil {
565+
db.preimages.commit(false)
613566
}
614567
// Keep committing nodes from the flush-list until we're below allowance
615568
oldest := db.oldest
@@ -644,13 +597,6 @@ func (db *Database) Cap(limit common.StorageSize) error {
644597
db.lock.Lock()
645598
defer db.lock.Unlock()
646599

647-
if flushPreimages {
648-
if db.preimages == nil {
649-
log.Error("Attempted to reset preimage cache whilst disabled")
650-
} else {
651-
db.preimages, db.preimagesSize = make(map[common.Hash][]byte), 0
652-
}
653-
}
654600
for db.oldest != oldest {
655601
node := db.dirties[db.oldest]
656602
delete(db.dirties, db.oldest)
@@ -694,13 +640,7 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H
694640

695641
// Move all of the accumulated preimages into a write batch
696642
if db.preimages != nil {
697-
rawdb.WritePreimages(batch, db.preimages)
698-
// Since we're going to replay trie node writes into the clean cache, flush out
699-
// any batched pre-images before continuing.
700-
if err := batch.Write(); err != nil {
701-
return err
702-
}
703-
batch.Reset()
643+
db.preimages.commit(true)
704644
}
705645
// Move the trie itself into the batch, flushing if enough data is accumulated
706646
nodes, storage := len(db.dirties), db.dirtiesSize
@@ -723,9 +663,6 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H
723663
batch.Reset()
724664

725665
// Reset the storage counters and bumped metrics
726-
if db.preimages != nil {
727-
db.preimages, db.preimagesSize = make(map[common.Hash][]byte), 0
728-
}
729666
memcacheCommitTimeTimer.Update(time.Since(start))
730667
memcacheCommitSizeMeter.Mark(int64(storage - db.dirtiesSize))
731668
memcacheCommitNodesMeter.Mark(int64(nodes - len(db.dirties)))
@@ -837,7 +774,11 @@ func (db *Database) Size() (common.StorageSize, common.StorageSize) {
837774
// counted.
838775
var metadataSize = common.StorageSize((len(db.dirties) - 1) * cachedNodeSize)
839776
var metarootRefs = common.StorageSize(len(db.dirties[common.Hash{}].children) * (common.HashLength + 2))
840-
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, db.preimagesSize
777+
var preimageSize common.StorageSize
778+
if db.preimages != nil {
779+
preimageSize = db.preimages.size()
780+
}
781+
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, preimageSize
841782
}
842783

843784
// saveCache saves clean state cache to given directory path

trie/preimages.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2022 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package trie
18+
19+
import (
20+
"sync"
21+
22+
"github.com/ethereum/go-ethereum/common"
23+
"github.com/ethereum/go-ethereum/core/rawdb"
24+
"github.com/ethereum/go-ethereum/ethdb"
25+
)
26+
27+
// preimageStore is the store for caching preimages of node key.
28+
type preimageStore struct {
29+
lock sync.RWMutex
30+
disk ethdb.KeyValueStore
31+
preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
32+
preimagesSize common.StorageSize // Storage size of the preimages cache
33+
}
34+
35+
// newPreimageStore initializes the store for caching preimages.
36+
func newPreimageStore(disk ethdb.KeyValueStore) *preimageStore {
37+
return &preimageStore{
38+
disk: disk,
39+
preimages: make(map[common.Hash][]byte),
40+
}
41+
}
42+
43+
// insertPreimage writes a new trie node pre-image to the memory database if it's
44+
// yet unknown. The method will NOT make a copy of the slice, only use if the
45+
// preimage will NOT be changed later on.
46+
func (store *preimageStore) insertPreimage(preimages map[common.Hash][]byte) {
47+
store.lock.Lock()
48+
defer store.lock.Unlock()
49+
50+
for hash, preimage := range preimages {
51+
if _, ok := store.preimages[hash]; ok {
52+
continue
53+
}
54+
store.preimages[hash] = preimage
55+
store.preimagesSize += common.StorageSize(common.HashLength + len(preimage))
56+
}
57+
}
58+
59+
// preimage retrieves a cached trie node pre-image from memory. If it cannot be
60+
// found cached, the method queries the persistent database for the content.
61+
func (store *preimageStore) preimage(hash common.Hash) []byte {
62+
store.lock.RLock()
63+
preimage := store.preimages[hash]
64+
store.lock.RUnlock()
65+
66+
if preimage != nil {
67+
return preimage
68+
}
69+
return rawdb.ReadPreimage(store.disk, hash)
70+
}
71+
72+
// commit flushes the cached preimages into the disk.
73+
func (store *preimageStore) commit(force bool) error {
74+
store.lock.Lock()
75+
defer store.lock.Unlock()
76+
77+
if store.preimagesSize <= 4*1024*1024 && !force {
78+
return nil
79+
}
80+
batch := store.disk.NewBatch()
81+
rawdb.WritePreimages(batch, store.preimages)
82+
if err := batch.Write(); err != nil {
83+
return err
84+
}
85+
store.preimages, store.preimagesSize = make(map[common.Hash][]byte), 0
86+
return nil
87+
}
88+
89+
// size returns the current storage size of accumulated preimages.
90+
func (store *preimageStore) size() common.StorageSize {
91+
store.lock.RLock()
92+
defer store.lock.RUnlock()
93+
94+
return store.preimagesSize
95+
}

trie/secure_trie.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
// SecureTrie is not safe for concurrent use.
3838
type SecureTrie struct {
3939
trie Trie
40+
preimages *preimageStore
4041
hashKeyBuf [common.HashLength]byte
4142
secKeyCache map[string][]byte
4243
secKeyCacheOwner *SecureTrie // Pointer to self, replace the key cache on mismatch
@@ -61,7 +62,7 @@ func NewSecure(owner common.Hash, root common.Hash, db *Database) (*SecureTrie,
6162
if err != nil {
6263
return nil, err
6364
}
64-
return &SecureTrie{trie: *trie}, nil
65+
return &SecureTrie{trie: *trie, preimages: db.preimages}, nil
6566
}
6667

6768
// Get returns the value for key stored in the trie.
@@ -153,7 +154,10 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte {
153154
if key, ok := t.getSecKeyCache()[string(shaKey)]; ok {
154155
return key
155156
}
156-
return t.trie.db.preimage(common.BytesToHash(shaKey))
157+
if t.preimages == nil {
158+
return nil
159+
}
160+
return t.preimages.preimage(common.BytesToHash(shaKey))
157161
}
158162

159163
// Commit writes all nodes and the secure hash pre-images to the trie's database.
@@ -164,12 +168,12 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte {
164168
func (t *SecureTrie) Commit(onleaf LeafCallback) (common.Hash, int, error) {
165169
// Write all the pre-images to the actual disk database
166170
if len(t.getSecKeyCache()) > 0 {
167-
if t.trie.db.preimages != nil { // Ugly direct check but avoids the below write lock
168-
t.trie.db.lock.Lock()
171+
if t.preimages != nil {
172+
preimages := make(map[common.Hash][]byte)
169173
for hk, key := range t.secKeyCache {
170-
t.trie.db.insertPreimage(common.BytesToHash([]byte(hk)), key)
174+
preimages[common.BytesToHash([]byte(hk))] = key
171175
}
172-
t.trie.db.lock.Unlock()
176+
t.preimages.insertPreimage(preimages)
173177
}
174178
t.secKeyCache = make(map[string][]byte)
175179
}
@@ -187,6 +191,7 @@ func (t *SecureTrie) Hash() common.Hash {
187191
func (t *SecureTrie) Copy() *SecureTrie {
188192
return &SecureTrie{
189193
trie: *t.trie.Copy(),
194+
preimages: t.preimages,
190195
secKeyCache: t.secKeyCache,
191196
}
192197
}

0 commit comments

Comments
 (0)