Skip to content

Commit 3cb4d0e

Browse files
authored
core: move overlay conversion code to its own file (#266)
1 parent 79d23d0 commit 3cb4d0e

File tree

2 files changed

+248
-210
lines changed

2 files changed

+248
-210
lines changed

core/overlay_transition.go

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
// Copyright 2023 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 core
18+
19+
import (
20+
"bufio"
21+
"bytes"
22+
"fmt"
23+
"io"
24+
"os"
25+
"time"
26+
27+
"github.com/ethereum/go-ethereum/common"
28+
"github.com/ethereum/go-ethereum/core/rawdb"
29+
"github.com/ethereum/go-ethereum/core/state"
30+
"github.com/ethereum/go-ethereum/core/types"
31+
"github.com/ethereum/go-ethereum/crypto"
32+
"github.com/ethereum/go-ethereum/log"
33+
"github.com/ethereum/go-ethereum/rlp"
34+
"github.com/ethereum/go-ethereum/trie"
35+
)
36+
37+
// OverlayVerkleTransition contains the overlay conversion logic
38+
func OverlayVerkleTransition(statedb *state.StateDB) error {
39+
migrdb := statedb.Database()
40+
41+
// verkle transition: if the conversion process is in progress, move
42+
// N values from the MPT into the verkle tree.
43+
if migrdb.InTransition() {
44+
var (
45+
now = time.Now()
46+
tt = statedb.GetTrie().(*trie.TransitionTrie)
47+
mpt = tt.Base()
48+
vkt = tt.Overlay()
49+
hasPreimagesBin = false
50+
preimageSeek = migrdb.GetCurrentPreimageOffset()
51+
fpreimages *bufio.Reader
52+
)
53+
54+
// TODO: avoid opening the preimages file here and make it part of, potentially, statedb.Database().
55+
filePreimages, err := os.Open("preimages.bin")
56+
if err != nil {
57+
// fallback on reading the db
58+
log.Warn("opening preimage file", "error", err)
59+
} else {
60+
defer filePreimages.Close()
61+
if _, err := filePreimages.Seek(preimageSeek, io.SeekStart); err != nil {
62+
return fmt.Errorf("seeking preimage file: %s", err)
63+
}
64+
fpreimages = bufio.NewReader(filePreimages)
65+
hasPreimagesBin = true
66+
}
67+
68+
accIt, err := statedb.Snaps().AccountIterator(mpt.Hash(), migrdb.GetCurrentAccountHash())
69+
if err != nil {
70+
return err
71+
}
72+
defer accIt.Release()
73+
accIt.Next()
74+
75+
// If we're about to start with the migration process, we have to read the first account hash preimage.
76+
if migrdb.GetCurrentAccountAddress() == nil {
77+
var addr common.Address
78+
if hasPreimagesBin {
79+
if _, err := io.ReadFull(fpreimages, addr[:]); err != nil {
80+
return fmt.Errorf("reading preimage file: %s", err)
81+
}
82+
} else {
83+
addr = common.BytesToAddress(rawdb.ReadPreimage(migrdb.DiskDB(), accIt.Hash()))
84+
if len(addr) != 20 {
85+
return fmt.Errorf("addr len is zero is not 32: %d", len(addr))
86+
}
87+
}
88+
migrdb.SetCurrentAccountAddress(addr)
89+
if migrdb.GetCurrentAccountHash() != accIt.Hash() {
90+
return fmt.Errorf("preimage file does not match account hash: %s != %s", crypto.Keccak256Hash(addr[:]), accIt.Hash())
91+
}
92+
preimageSeek += int64(len(addr))
93+
}
94+
95+
const maxMovedCount = 10000
96+
// mkv will be assiting in the collection of up to maxMovedCount key values to be migrated to the VKT.
97+
// It has internal caches to do efficient MPT->VKT key calculations, which will be discarded after
98+
// this function.
99+
mkv := newKeyValueMigrator()
100+
// move maxCount accounts into the verkle tree, starting with the
101+
// slots from the previous account.
102+
count := 0
103+
104+
// if less than maxCount slots were moved, move to the next account
105+
for count < maxMovedCount {
106+
acc, err := types.FullAccount(accIt.Account())
107+
if err != nil {
108+
log.Error("Invalid account encountered during traversal", "error", err)
109+
return err
110+
}
111+
vkt.SetStorageRootConversion(*migrdb.GetCurrentAccountAddress(), acc.Root)
112+
113+
// Start with processing the storage, because once the account is
114+
// converted, the `stateRoot` field loses its meaning. Which means
115+
// that it opens the door to a situation in which the storage isn't
116+
// converted, but it can not be found since the account was and so
117+
// there is no way to find the MPT storage from the information found
118+
// in the verkle account.
119+
// Note that this issue can still occur if the account gets written
120+
// to during normal block execution. A mitigation strategy has been
121+
// introduced with the `*StorageRootConversion` fields in VerkleDB.
122+
if acc.HasStorage() {
123+
stIt, err := statedb.Snaps().StorageIterator(mpt.Hash(), accIt.Hash(), migrdb.GetCurrentSlotHash())
124+
if err != nil {
125+
return err
126+
}
127+
stIt.Next()
128+
129+
// fdb.StorageProcessed will be initialized to `true` if the
130+
// entire storage for an account was not entirely processed
131+
// by the previous block. This is used as a signal to resume
132+
// processing the storage for that account where we left off.
133+
// If the entire storage was processed, then the iterator was
134+
// created in vain, but it's ok as this will not happen often.
135+
for ; !migrdb.GetStorageProcessed() && count < maxMovedCount; count++ {
136+
var (
137+
value []byte // slot value after RLP decoding
138+
safeValue [32]byte // 32-byte aligned value
139+
)
140+
if err := rlp.DecodeBytes(stIt.Slot(), &value); err != nil {
141+
return fmt.Errorf("error decoding bytes %x: %w", stIt.Slot(), err)
142+
}
143+
copy(safeValue[32-len(value):], value)
144+
145+
var slotnr []byte
146+
if hasPreimagesBin {
147+
var s [32]byte
148+
slotnr = s[:]
149+
if _, err := io.ReadFull(fpreimages, slotnr); err != nil {
150+
return fmt.Errorf("reading preimage file: %s", err)
151+
}
152+
} else {
153+
slotnr = rawdb.ReadPreimage(migrdb.DiskDB(), stIt.Hash())
154+
if len(slotnr) != 32 {
155+
return fmt.Errorf("slotnr len is zero is not 32: %d", len(slotnr))
156+
}
157+
}
158+
if crypto.Keccak256Hash(slotnr[:]) != stIt.Hash() {
159+
return fmt.Errorf("preimage file does not match storage hash: %s!=%s", crypto.Keccak256Hash(slotnr), stIt.Hash())
160+
}
161+
preimageSeek += int64(len(slotnr))
162+
163+
mkv.addStorageSlot(migrdb.GetCurrentAccountAddress().Bytes(), slotnr, safeValue[:])
164+
165+
// advance the storage iterator
166+
migrdb.SetStorageProcessed(!stIt.Next())
167+
if !migrdb.GetStorageProcessed() {
168+
migrdb.SetCurrentSlotHash(stIt.Hash())
169+
}
170+
}
171+
stIt.Release()
172+
}
173+
174+
// If the maximum number of leaves hasn't been reached, then
175+
// it means that the storage has finished processing (or none
176+
// was available for this account) and that the account itself
177+
// can be processed.
178+
if count < maxMovedCount {
179+
count++ // count increase for the account itself
180+
181+
mkv.addAccount(migrdb.GetCurrentAccountAddress().Bytes(), acc)
182+
vkt.ClearStrorageRootConversion(*migrdb.GetCurrentAccountAddress())
183+
184+
// Store the account code if present
185+
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) {
186+
code := rawdb.ReadCode(statedb.Database().DiskDB(), common.BytesToHash(acc.CodeHash))
187+
chunks := trie.ChunkifyCode(code)
188+
189+
mkv.addAccountCode(migrdb.GetCurrentAccountAddress().Bytes(), uint64(len(code)), chunks)
190+
}
191+
192+
// reset storage iterator marker for next account
193+
migrdb.SetStorageProcessed(false)
194+
migrdb.SetCurrentSlotHash(common.Hash{})
195+
196+
// Move to the next account, if available - or end
197+
// the transition otherwise.
198+
if accIt.Next() {
199+
var addr common.Address
200+
if hasPreimagesBin {
201+
if _, err := io.ReadFull(fpreimages, addr[:]); err != nil {
202+
return fmt.Errorf("reading preimage file: %s", err)
203+
}
204+
} else {
205+
addr = common.BytesToAddress(rawdb.ReadPreimage(migrdb.DiskDB(), accIt.Hash()))
206+
if len(addr) != 20 {
207+
return fmt.Errorf("account address len is zero is not 20: %d", len(addr))
208+
}
209+
}
210+
// fmt.Printf("account switch: %s != %s\n", crypto.Keccak256Hash(addr[:]), accIt.Hash())
211+
if crypto.Keccak256Hash(addr[:]) != accIt.Hash() {
212+
return fmt.Errorf("preimage file does not match account hash: %s != %s", crypto.Keccak256Hash(addr[:]), accIt.Hash())
213+
}
214+
preimageSeek += int64(len(addr))
215+
migrdb.SetCurrentAccountAddress(addr)
216+
} else {
217+
// case when the account iterator has
218+
// reached the end but count < maxCount
219+
migrdb.EndVerkleTransition()
220+
break
221+
}
222+
}
223+
}
224+
migrdb.SetCurrentPreimageOffset(preimageSeek)
225+
226+
log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account", statedb.Database().GetCurrentAccountHash())
227+
228+
// Take all the collected key-values and prepare the new leaf values.
229+
// This fires a background routine that will start doing the work that
230+
// migrateCollectedKeyValues() will use to insert into the tree.
231+
//
232+
// TODO: Now both prepare() and migrateCollectedKeyValues() are next to each other, but
233+
// after we fix an existing bug, we can call prepare() before the block execution and
234+
// let it do the work in the background. After the block execution and finalization
235+
// finish, we can call migrateCollectedKeyValues() which should already find everything ready.
236+
mkv.prepare()
237+
now = time.Now()
238+
if err := mkv.migrateCollectedKeyValues(tt.Overlay()); err != nil {
239+
return fmt.Errorf("could not migrate key values: %w", err)
240+
}
241+
log.Info("Inserted key values in overlay tree", "count", count, "duration", time.Since(now))
242+
}
243+
244+
return nil
245+
}

0 commit comments

Comments
 (0)