From 14839c9113a421b2ddf6b39205257ad1f5dee3cf Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Sat, 7 Feb 2026 20:39:44 +0100 Subject: [PATCH] fix prefetcher issue + reader bootstrap and get rid of triedb.IsVerkle() checks (#564) - Fix an issue in which the prefetcher was creating a new MPT tree in verkle mode, based on the fact that triedb.IsVerkle() was false. - Also fix the tree bootstrapping in the reader - generally, no longer use db.triedb.IsVerkle() as it's only true if the db is used when starting in verkle mode. --- core/state/reader.go | 13 +++++++++---- core/state/state_object.go | 3 +++ core/state/statedb.go | 14 +++++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/core/state/reader.go b/core/state/reader.go index fd999b11ac..74ff665804 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -318,17 +318,22 @@ func newTrieReader(root common.Hash, db *triedb.Database, ts *overlay.Transition tr Trie err error ) - if !db.IsVerkle() { + if !db.IsVerkle() && (ts == nil || !ts.InTransition()) { tr, err = trie.NewStateTrie(trie.StateTrieID(root), db) } else { - // When IsVerkle() is true, create a BinaryTrie wrapped in TransitionTrie - binTrie, binErr := bintrie.NewBinaryTrie(root, db) + var binTrie *bintrie.BinaryTrie + var binErr error + if ts.BaseRoot == (common.Hash{}) { + binTrie, binErr = bintrie.NewBinaryTrie(common.Hash{}, db) + } else { + binTrie, binErr = bintrie.NewBinaryTrie(root, db) + } if binErr != nil { return nil, binErr } // Based on the transition status, determine if the overlay - // tree needs to be created, or if a single, target tree is + // tree needs to be created, or if a single target tree is // to be picked. if ts.InTransition() { mpt, err := trie.NewStateTrie(trie.StateTrieID(ts.BaseRoot), db) diff --git a/core/state/state_object.go b/core/state/state_object.go index 2873c3cb8a..418014b0d0 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -147,6 +147,9 @@ func (s *stateObject) getTrie() (Trie, error) { func (s *stateObject) getPrefetchedTrie() Trie { // If there's nothing to meaningfully return, let the user figure it out by // pulling the trie from disk. + if s.db.trie != nil && s.db.trie.IsVerkle() { + return nil + } if (s.data.Root == types.EmptyRootHash && !s.db.db.TrieDB().IsVerkle()) || s.db.prefetcher == nil { return nil } diff --git a/core/state/statedb.go b/core/state/statedb.go index 61d80df56e..8dfe979b77 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -356,6 +356,10 @@ func (s *StateDB) InTransition() bool { return completeValue != (common.Hash{}) } +func (s *StateDB) isVerkle() bool { + return s.db.TrieDB().IsVerkle() || (s.trie != nil && s.trie.IsVerkle()) +} + // TxIndex returns the current transaction index set by SetTxContext. func (s *StateDB) TxIndex() int { return s.txIndex @@ -830,7 +834,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { start = time.Now() workers errgroup.Group ) - if s.db.TrieDB().IsVerkle() { + if s.isVerkle() { // Whilst MPT storage tries are independent, Verkle has one single trie // for all the accounts and all the storage slots merged together. The // former can thus be simply parallelized, but updating the latter will @@ -844,7 +848,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { } obj := s.stateObjects[addr] // closure for the task runner below workers.Go(func() error { - if s.db.TrieDB().IsVerkle() { + if s.isVerkle() { obj.updateTrie() } else { obj.updateRoot() @@ -861,7 +865,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // If witness building is enabled, gather all the read-only accesses. // Skip witness collection in Verkle mode, they will be gathered // together at the end. - if s.witness != nil && !s.db.TrieDB().IsVerkle() { + if s.witness != nil && !s.isVerkle() { // Pull in anything that has been accessed before destruction for _, obj := range s.stateObjectsDestruct { // Skip any objects that haven't touched their storage @@ -918,7 +922,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // only a single trie is used for state hashing. Replacing a non-nil verkle tree // here could result in losing uncommitted changes from storage. start = time.Now() - if s.prefetcher != nil { + if s.prefetcher != nil && !s.isVerkle() { if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil { log.Error("Failed to retrieve account pre-fetcher trie") } else { @@ -1137,7 +1141,7 @@ func (s *StateDB) handleDestruction(noStorageWiping bool) (map[common.Hash]*acco deletes[addrHash] = op // Short circuit if the origin storage was empty. - if prev.Root == types.EmptyRootHash || s.db.TrieDB().IsVerkle() { + if prev.Root == types.EmptyRootHash || s.isVerkle() { continue } if noStorageWiping {