Skip to content

Commit 4b4aa39

Browse files
rjl493456442mask-pp
authored andcommitted
core/state: fix trie prefetcher for verkle (ethereum#30354)
This pull request fixes the panic issue in prefetcher once the verkle is activated.
1 parent 0d08b7e commit 4b4aa39

File tree

3 files changed

+92
-20
lines changed

3 files changed

+92
-20
lines changed

core/state/statedb.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -887,8 +887,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
887887
return nil
888888
})
889889
}
890-
// If witness building is enabled, gather all the read-only accesses
891-
if s.witness != nil {
890+
// If witness building is enabled, gather all the read-only accesses.
891+
// Skip witness collection in Verkle mode, they will be gathered
892+
// together at the end.
893+
if s.witness != nil && !s.db.TrieDB().IsVerkle() {
892894
// Pull in anything that has been accessed before destruction
893895
for _, obj := range s.stateObjectsDestruct {
894896
// Skip any objects that haven't touched their storage
@@ -929,7 +931,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
929931
// only a single trie is used for state hashing. Replacing a non-nil verkle tree
930932
// here could result in losing uncommitted changes from storage.
931933
start = time.Now()
932-
if s.prefetcher != nil && (s.trie == nil || !s.trie.IsVerkle()) {
934+
if s.prefetcher != nil {
933935
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
934936
log.Error("Failed to retrieve account pre-fetcher trie")
935937
} else {

core/state/trie_prefetcher.go

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ var (
4040
//
4141
// Note, the prefetcher's API is not thread safe.
4242
type triePrefetcher struct {
43+
verkle bool // Flag whether the prefetcher is in verkle mode
4344
db Database // Database to fetch trie nodes through
4445
root common.Hash // Root hash of the account trie for metrics
4546
fetchers map[string]*subfetcher // Subfetchers for each trie
@@ -66,6 +67,7 @@ type triePrefetcher struct {
6667
func newTriePrefetcher(db Database, root common.Hash, namespace string, noreads bool) *triePrefetcher {
6768
prefix := triePrefetchMetricsPrefix + namespace
6869
return &triePrefetcher{
70+
verkle: db.TrieDB().IsVerkle(),
6971
db: db,
7072
root: root,
7173
fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map
@@ -196,12 +198,18 @@ func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie {
196198
func (p *triePrefetcher) used(owner common.Hash, root common.Hash, used [][]byte) {
197199
if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil {
198200
fetcher.wait() // ensure the fetcher's idle before poking in its internals
199-
fetcher.used = used
201+
fetcher.used = append(fetcher.used, used...)
200202
}
201203
}
202204

203205
// trieID returns an unique trie identifier consists the trie owner and root hash.
204206
func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string {
207+
// The trie in verkle is only identified by state root
208+
if p.verkle {
209+
return p.root.Hex()
210+
}
211+
// The trie in merkle is either identified by state root (account trie),
212+
// or identified by the owner and trie root (storage trie)
205213
trieID := make([]byte, common.HashLength*2)
206214
copy(trieID, owner.Bytes())
207215
copy(trieID[common.HashLength:], root.Bytes())
@@ -320,29 +328,47 @@ func (sf *subfetcher) terminate(async bool) {
320328
<-sf.term
321329
}
322330

331+
// openTrie resolves the target trie from database for prefetching.
332+
func (sf *subfetcher) openTrie() error {
333+
// Open the verkle tree if the sub-fetcher is in verkle mode. Note, there is
334+
// only a single fetcher for verkle.
335+
if sf.db.TrieDB().IsVerkle() {
336+
tr, err := sf.db.OpenTrie(sf.state)
337+
if err != nil {
338+
log.Warn("Trie prefetcher failed opening verkle trie", "root", sf.root, "err", err)
339+
return err
340+
}
341+
sf.trie = tr
342+
return nil
343+
}
344+
// Open the merkle tree if the sub-fetcher is in merkle mode
345+
if sf.owner == (common.Hash{}) {
346+
tr, err := sf.db.OpenTrie(sf.state)
347+
if err != nil {
348+
log.Warn("Trie prefetcher failed opening account trie", "root", sf.root, "err", err)
349+
return err
350+
}
351+
sf.trie = tr
352+
return nil
353+
}
354+
tr, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
355+
if err != nil {
356+
log.Warn("Trie prefetcher failed opening storage trie", "root", sf.root, "err", err)
357+
return err
358+
}
359+
sf.trie = tr
360+
return nil
361+
}
362+
323363
// loop loads newly-scheduled trie tasks as they are received and loads them, stopping
324364
// when requested.
325365
func (sf *subfetcher) loop() {
326366
// No matter how the loop stops, signal anyone waiting that it's terminated
327367
defer close(sf.term)
328368

329-
// Start by opening the trie and stop processing if it fails
330-
if sf.owner == (common.Hash{}) {
331-
trie, err := sf.db.OpenTrie(sf.root)
332-
if err != nil {
333-
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
334-
return
335-
}
336-
sf.trie = trie
337-
} else {
338-
trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
339-
if err != nil {
340-
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
341-
return
342-
}
343-
sf.trie = trie
369+
if err := sf.openTrie(); err != nil {
370+
return
344371
}
345-
// Trie opened successfully, keep prefetching items
346372
for {
347373
select {
348374
case <-sf.wake:

core/state/trie_prefetcher_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import (
2424
"github.com/ethereum/go-ethereum/core/rawdb"
2525
"github.com/ethereum/go-ethereum/core/tracing"
2626
"github.com/ethereum/go-ethereum/core/types"
27+
"github.com/ethereum/go-ethereum/crypto"
28+
"github.com/ethereum/go-ethereum/internal/testrand"
29+
"github.com/ethereum/go-ethereum/triedb"
2730
"github.com/holiman/uint256"
2831
)
2932

@@ -62,3 +65,44 @@ func TestUseAfterTerminate(t *testing.T) {
6265
t.Errorf("Prefetcher returned nil trie after terminate")
6366
}
6467
}
68+
69+
func TestVerklePrefetcher(t *testing.T) {
70+
db := NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), triedb.VerkleDefaults)
71+
state, err := New(types.EmptyRootHash, db, nil)
72+
if err != nil {
73+
t.Fatalf("failed to initialize state: %v", err)
74+
}
75+
// Create an account and check if the retrieved balance is correct
76+
addr := testrand.Address()
77+
skey := testrand.Hash()
78+
sval := testrand.Hash()
79+
80+
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
81+
state.SetCode(addr, []byte("hello")) // Change an external metadata
82+
state.SetState(addr, skey, sval) // Change the storage trie
83+
root, _ := state.Commit(0, true)
84+
85+
state, _ = New(root, db, nil)
86+
sRoot := state.GetStorageRoot(addr)
87+
fetcher := newTriePrefetcher(db, root, "", false)
88+
89+
// Read account
90+
fetcher.prefetch(common.Hash{}, root, common.Address{}, [][]byte{
91+
addr.Bytes(),
92+
}, false)
93+
94+
// Read storage slot
95+
fetcher.prefetch(crypto.Keccak256Hash(addr.Bytes()), sRoot, addr, [][]byte{
96+
skey.Bytes(),
97+
}, false)
98+
99+
fetcher.terminate(false)
100+
accountTrie := fetcher.trie(common.Hash{}, root)
101+
storageTrie := fetcher.trie(crypto.Keccak256Hash(addr.Bytes()), sRoot)
102+
103+
rootA := accountTrie.Hash()
104+
rootB := storageTrie.Hash()
105+
if rootA != rootB {
106+
t.Fatal("Two different tries are retrieved")
107+
}
108+
}

0 commit comments

Comments
 (0)