Skip to content

Commit 0854d4b

Browse files
0xmountaintoprjl493456442mask-pp
authored
core, trie, eth, cmd: rework preimage store (#205)
* core, trie, eth, cmd: rework preimage store * fix ci * fix ci * Update zk_trie_database.go --------- Co-authored-by: rjl493456442 <garyrong0905@gmail.com> Co-authored-by: maskpp <maskpp266@gmail.com>
1 parent aae2ea5 commit 0854d4b

File tree

9 files changed

+139
-91
lines changed

9 files changed

+139
-91
lines changed

cmd/evm/internal/t8ntool/execution.go

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

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

core/state/state_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/scroll-tech/go-ethereum/common"
2525
"github.com/scroll-tech/go-ethereum/core/rawdb"
2626
"github.com/scroll-tech/go-ethereum/ethdb"
27+
"github.com/scroll-tech/go-ethereum/trie"
2728
)
2829

2930
type stateTest struct {
@@ -39,7 +40,7 @@ func newStateTest() *stateTest {
3940

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

4546
// generate a few entries

eth/api_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/scroll-tech/go-ethereum/core/rawdb"
3131
"github.com/scroll-tech/go-ethereum/core/state"
3232
"github.com/scroll-tech/go-ethereum/crypto"
33+
"github.com/scroll-tech/go-ethereum/trie"
3334
)
3435

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

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

trie/database.go

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

83-
preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
84-
8583
gctime time.Duration // Time spent on garbage collection since last commit
8684
gcnodes uint64 // Nodes garbage collected since last commit
8785
gcsize common.StorageSize // Data storage garbage collected since last commit
@@ -90,9 +88,9 @@ type Database struct {
9088
flushnodes uint64 // Nodes flushed since last commit
9189
flushsize common.StorageSize // Data storage flushed since last commit
9290

93-
dirtiesSize common.StorageSize // Storage size of the dirty node cache (exc. metadata)
94-
childrenSize common.StorageSize // Storage size of the external children tracking
95-
preimagesSize common.StorageSize // Storage size of the preimages cache
91+
dirtiesSize common.StorageSize // Storage size of the dirty node cache (exc. metadata)
92+
childrenSize common.StorageSize // Storage size of the external children tracking
93+
preimages *preimageStore // The store for caching preimages
9694

9795
lock sync.RWMutex
9896
}
@@ -305,16 +303,18 @@ func NewDatabaseWithConfig(diskdb ethdb.KeyValueStore, config *Config) *Database
305303
cleans = fastcache.LoadFromFileOrNew(config.Journal, config.Cache*1024*1024)
306304
}
307305
}
306+
var preimage *preimageStore
307+
if config != nil && config.Preimages {
308+
preimage = newPreimageStore(diskdb)
309+
}
308310
db := &Database{
309311
diskdb: diskdb,
310312
cleans: cleans,
311313
dirties: map[common.Hash]*cachedNode{{}: {
312314
children: make(map[common.Hash]uint16),
313315
}},
314316
rawDirties: make(KvMap),
315-
}
316-
if config == nil || config.Preimages { // TODO(karalabe): Flip to default off in the future
317-
db.preimages = make(map[common.Hash][]byte)
317+
preimages: preimage,
318318
}
319319
return db
320320
}
@@ -357,24 +357,6 @@ func (db *Database) insert(hash common.Hash, size int, node node) {
357357
db.dirtiesSize += common.StorageSize(common.HashLength + entry.size)
358358
}
359359

360-
// insertPreimage writes a new trie node pre-image to the memory database if it's
361-
// yet unknown. The method will NOT make a copy of the slice,
362-
// only use if the preimage will NOT be changed later on.
363-
//
364-
// Note, this method assumes that the database's lock is held!
365-
func (db *Database) insertPreimage(hash common.Hash, preimage []byte) {
366-
// Short circuit if preimage collection is disabled
367-
if db.preimages == nil {
368-
return
369-
}
370-
// Track the preimage if a yet unknown one
371-
if _, ok := db.preimages[hash]; ok {
372-
return
373-
}
374-
db.preimages[hash] = preimage
375-
db.preimagesSize += common.StorageSize(common.HashLength + len(preimage))
376-
}
377-
378360
// node retrieves a cached trie node from memory, or returns nil if none can be
379361
// found in the memory cache.
380362
func (db *Database) node(hash common.Hash) node {
@@ -451,24 +433,6 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
451433
return nil, errors.New("not found")
452434
}
453435

454-
// preimage retrieves a cached trie node pre-image from memory. If it cannot be
455-
// found cached, the method queries the persistent database for the content.
456-
func (db *Database) preimage(hash common.Hash) []byte {
457-
// Short circuit if preimage collection is disabled
458-
if db.preimages == nil {
459-
return nil
460-
}
461-
// Retrieve the node from cache if available
462-
db.lock.RLock()
463-
preimage := db.preimages[hash]
464-
db.lock.RUnlock()
465-
466-
if preimage != nil {
467-
return preimage
468-
}
469-
return rawdb.ReadPreimage(db.diskdb, hash)
470-
}
471-
472436
// Nodes retrieves the hashes of all the nodes cached within the memory database.
473437
// This method is extremely expensive and should only be used to validate internal
474438
// states in test code.
@@ -613,19 +577,8 @@ func (db *Database) Cap(limit common.StorageSize) error {
613577

614578
// If the preimage cache got large enough, push to disk. If it's still small
615579
// leave for later to deduplicate writes.
616-
flushPreimages := db.preimagesSize > 4*1024*1024
617-
if flushPreimages {
618-
if db.preimages == nil {
619-
log.Error("Attempted to write preimages whilst disabled")
620-
} else {
621-
rawdb.WritePreimages(batch, db.preimages)
622-
if batch.ValueSize() > ethdb.IdealBatchSize {
623-
if err := batch.Write(); err != nil {
624-
return err
625-
}
626-
batch.Reset()
627-
}
628-
}
580+
if db.preimages != nil {
581+
db.preimages.commit(false)
629582
}
630583
// Keep committing nodes from the flush-list until we're below allowance
631584
oldest := db.oldest
@@ -660,13 +613,6 @@ func (db *Database) Cap(limit common.StorageSize) error {
660613
db.lock.Lock()
661614
defer db.lock.Unlock()
662615

663-
if flushPreimages {
664-
if db.preimages == nil {
665-
log.Error("Attempted to reset preimage cache whilst disabled")
666-
} else {
667-
db.preimages, db.preimagesSize = make(map[common.Hash][]byte), 0
668-
}
669-
}
670616
for db.oldest != oldest {
671617
node := db.dirties[db.oldest]
672618
delete(db.dirties, db.oldest)
@@ -727,13 +673,7 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H
727673

728674
// Move all of the accumulated preimages into a write batch
729675
if db.preimages != nil {
730-
rawdb.WritePreimages(batch, db.preimages)
731-
// Since we're going to replay trie node writes into the clean cache, flush out
732-
// any batched pre-images before continuing.
733-
if err := batch.Write(); err != nil {
734-
return err
735-
}
736-
batch.Reset()
676+
db.preimages.commit(true)
737677
}
738678
// Move the trie itself into the batch, flushing if enough data is accumulated
739679
nodes, storage := len(db.dirties), db.dirtiesSize
@@ -756,9 +696,6 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H
756696
batch.Reset()
757697

758698
// Reset the storage counters and bumped metrics
759-
if db.preimages != nil {
760-
db.preimages, db.preimagesSize = make(map[common.Hash][]byte), 0
761-
}
762699
memcacheCommitTimeTimer.Update(time.Since(start))
763700
memcacheCommitSizeMeter.Mark(int64(storage - db.dirtiesSize))
764701
memcacheCommitNodesMeter.Mark(int64(nodes - len(db.dirties)))
@@ -870,7 +807,11 @@ func (db *Database) Size() (common.StorageSize, common.StorageSize) {
870807
// counted.
871808
var metadataSize = common.StorageSize((len(db.dirties) - 1) * cachedNodeSize)
872809
var metarootRefs = common.StorageSize(len(db.dirties[common.Hash{}].children) * (common.HashLength + 2))
873-
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, db.preimagesSize
810+
var preimageSize common.StorageSize
811+
if db.preimages != nil {
812+
preimageSize = db.preimages.size()
813+
}
814+
return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, preimageSize
874815
}
875816

876817
// 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/scroll-tech/go-ethereum/common"
23+
"github.com/scroll-tech/go-ethereum/core/rawdb"
24+
"github.com/scroll-tech/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: 10 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(root common.Hash, db *Database) (*SecureTrie, error) {
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
}

trie/zk_trie.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,10 @@ func (t *ZkTrie) GetKey(kHashBytes []byte) []byte {
112112
if err != nil {
113113
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
114114
}
115-
116-
return t.db.db.preimage(common.BytesToHash(k.Bytes()))
117-
115+
if t.db.db.preimages != nil {
116+
return t.db.db.preimages.preimage(common.BytesToHash(k.Bytes()))
117+
}
118+
return nil
118119
}
119120

120121
// Commit writes all nodes and the secure hash pre-images to the trie's database.

trie/zk_trie_database.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,8 @@ func (l *ZktrieDatabase) Get(key []byte) ([]byte, error) {
6666
func (l *ZktrieDatabase) UpdatePreimage(preimage []byte, hashField *big.Int) {
6767
db := l.db
6868
if db.preimages != nil { // Ugly direct check but avoids the below write lock
69-
db.lock.Lock()
7069
// we must copy the input key
71-
db.insertPreimage(common.BytesToHash(hashField.Bytes()), common.CopyBytes(preimage))
72-
db.lock.Unlock()
70+
db.preimages.insertPreimage(map[common.Hash][]byte{common.BytesToHash(hashField.Bytes()): common.CopyBytes(preimage)})
7371
}
7472
}
7573

trie/zk_trie_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,14 @@ import (
3535
)
3636

3737
func newEmptyZkTrie() *ZkTrie {
38-
trie, _ := NewZkTrie(common.Hash{}, NewZktrieDatabase(memorydb.New()))
38+
trie, _ := NewZkTrie(
39+
common.Hash{},
40+
&ZktrieDatabase{
41+
db: NewDatabaseWithConfig(memorydb.New(),
42+
&Config{Preimages: true}),
43+
prefix: []byte{},
44+
},
45+
)
3946
return trie
4047
}
4148

0 commit comments

Comments
 (0)