Skip to content

Commit 09494c6

Browse files
gballetjsign
andauthored
overlay transition (#244)
* overlay transition Fix some bugs identified in the code review Co-authored-by: Ignacio Hagopian <[email protected]> Include base -> overlay key-values migration logic (#199) * mod: add go-verkle version with key-value migration new apis Signed-off-by: Ignacio Hagopian <[email protected]> * core/stateprocessor: use constant for max number of migrated key-values Signed-off-by: Ignacio Hagopian <[email protected]> * core: add base->overlay key-values migration logic Signed-off-by: Ignacio Hagopian <[email protected]> * core: fix some compiler errors Signed-off-by: Ignacio Hagopian <[email protected]> * trie: consider removing transition trie api in the future Signed-off-by: Ignacio Hagopian <[email protected]> * mod: use latest go-verkle Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> fix some unit tests errors get convresion block from file fix compilation issues fix initialization issue in migrator fix: changes needed to run the first 28 blocks important sutff: fix the banner fix: use nonce instead of balance in nonce leaf (#202) fixes for performing the overlay transition (#203) * fixes for performing the overlay transition * fixes for the full replay * fix: deletion-and-recreation of EoA * fixes to replay 2M+ blocks * upgrade to go-verkle@master * fix: proper number of chunk evals * rewrite conversion loop to fix known issues changes to make replay work with the overlay method (#216) * fixes for performing the overlay transition fixes for the full replay fix: deletion-and-recreation of EoA fixes to replay 2M+ blocks upgrade to go-verkle@master fix: proper number of chunk evals rewrite conversion loop to fix known issues changes to make replay work with the overlay method fixes to replay 2M+ blocks update to latest go-verkle@master * use a PBSS-like scheme for internal nodes (#221) * use a PBSS-like scheme for internal nodes * a couple of fixes coming from debugging replay * fix: use an error to notify the transition tree that a deleted account was found in the overlay tree (#222) * fixes for pbss replay (#227) * fixes for pbss replay * trie/verkle: use capped batch size (#229) * trie/verkle: use capped batch size Signed-off-by: Ignacio Hagopian <[email protected]> * trie/verkle: avoid path variable allocation per db.Put Signed-off-by: Ignacio Hagopian <[email protected]> * don't keep more than 32 state root conversions in RAM (#230) --------- Signed-off-by: Ignacio Hagopian <[email protected]> Co-authored-by: Guillaume Ballet <[email protected]> * cleanup some code * mod: update go-verkle Signed-off-by: Ignacio Hagopian <[email protected]> * re-enable snapshot (#231) * re-enable cancun block / snapshot (#226) * clear storage conversion key upon translating account (#234) * clear storage conversion key upon translating account * mod: use latest go-verkle Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> Co-authored-by: Ignacio Hagopian <[email protected]> * fix: self-deadlock with translated root map mutex (#236) * return compressed commitment as root commitment (#237) --------- Signed-off-by: Ignacio Hagopian <[email protected]> Co-authored-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> Co-authored-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> Co-authored-by: Ignacio Hagopian <[email protected]> fix first panic in *TransitionTrie.Copy() upgrade go-verkle to latest master mod: update go-verkle (#239) Signed-off-by: Ignacio Hagopian <[email protected]> core: print state root every 100 blocks (#240) Signed-off-by: Ignacio Hagopian <[email protected]> fix: only Commit the account trie (#242) fixes to get TestProcessVerkle to work with the overlay branch (#238) * fixes to get TestProcessVerkle to work with the overlay branch * fix all panics in verkle state processor test * fix proof verification move transition management to cachingDB * fix: mark the verkle transition as started if it's ended without being started * fix the verkle state processing test * fix linter errors * Add a function to clear verkle params for replay * fix: handle TransitionTrie in OpenStorageTrie * fix linter issue * fix the deleted account error (#247) * code cleanup (#248) * fix: don't error on a missing conversion.txt (#249) * Overlay Tree preimages exporting and usage (#246) * export overlay preimages tool Signed-off-by: Ignacio Hagopian <[email protected]> * use preimages flat file in overlay tree migration logic Signed-off-by: Ignacio Hagopian <[email protected]> * cmd/geth: add --roothash to overlay tree preimage exporting command Signed-off-by: Ignacio Hagopian <[email protected]> * cleanup Signed-off-by: Ignacio Hagopian <[email protected]> * review feedback Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> * fix: reduce the PR footprint (#250) * fix: don't fail when preimages.bin is missing (#251) * fix: don't fail when preimages.bin is missing * fix: don't open the preimages file when outside of transition --------- Signed-off-by: Ignacio Hagopian <[email protected]> Co-authored-by: Ignacio Hagopian <[email protected]>
1 parent 0a7efe1 commit 09494c6

23 files changed

+1210
-115
lines changed

cmd/geth/chaincmd.go

+38
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,17 @@ It's deprecated, please use "geth db import" instead.
143143
Description: `
144144
The export-preimages command exports hash preimages to an RLP encoded stream.
145145
It's deprecated, please use "geth db export" instead.
146+
`,
147+
}
148+
exportOverlayPreimagesCommand = &cli.Command{
149+
Action: exportOverlayPreimages,
150+
Name: "export-overlay-preimages",
151+
Usage: "Export the preimage in overlay tree migration order",
152+
ArgsUsage: "<dumpfile>",
153+
Flags: flags.Merge([]cli.Flag{utils.TreeRootFlag}, utils.DatabasePathFlags),
154+
Description: `
155+
The export-overlay-preimages command exports hash preimages to a flat file, in exactly
156+
the expected order for the overlay tree migration.
146157
`,
147158
}
148159
dumpCommand = &cli.Command{
@@ -394,6 +405,33 @@ func exportPreimages(ctx *cli.Context) error {
394405
return nil
395406
}
396407

408+
// exportOverlayPreimages dumps the preimage data to a flat file.
409+
func exportOverlayPreimages(ctx *cli.Context) error {
410+
if ctx.Args().Len() < 1 {
411+
utils.Fatalf("This command requires an argument.")
412+
}
413+
stack, _ := makeConfigNode(ctx)
414+
defer stack.Close()
415+
416+
chain, _ := utils.MakeChain(ctx, stack)
417+
418+
var root common.Hash
419+
if ctx.String(utils.TreeRootFlag.Name) != "" {
420+
rootBytes := common.FromHex(ctx.String(utils.StartKeyFlag.Name))
421+
if len(rootBytes) != common.HashLength {
422+
return fmt.Errorf("invalid root hash length")
423+
}
424+
root = common.BytesToHash(rootBytes)
425+
}
426+
427+
start := time.Now()
428+
if err := utils.ExportOverlayPreimages(chain, ctx.Args().First(), root); err != nil {
429+
utils.Fatalf("Export error: %v\n", err)
430+
}
431+
fmt.Printf("Export done in %v\n", time.Since(start))
432+
return nil
433+
}
434+
397435
func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) {
398436
db := utils.MakeChainDatabase(ctx, stack, true)
399437
var header *types.Header

cmd/geth/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ func init() {
213213
exportCommand,
214214
importPreimagesCommand,
215215
exportPreimagesCommand,
216+
exportOverlayPreimagesCommand,
216217
removedbCommand,
217218
dumpCommand,
218219
dumpGenesisCommand,

cmd/geth/verkle.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,13 @@ func convertToVerkle(ctx *cli.Context) error {
130130
vRoot = verkle.New().(*verkle.InternalNode)
131131
)
132132

133-
saveverkle := func(node verkle.VerkleNode) {
134-
comm := node.Commit()
133+
saveverkle := func(path []byte, node verkle.VerkleNode) {
134+
node.Commit()
135135
s, err := node.Serialize()
136136
if err != nil {
137137
panic(err)
138138
}
139-
commB := comm.Bytes()
140-
if err := chaindb.Put(commB[:], s); err != nil {
139+
if err := chaindb.Put(path, s); err != nil {
141140
panic(err)
142141
}
143142
}
@@ -330,7 +329,7 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error
330329
return fmt.Errorf("could not find child %x in db: %w", childC, err)
331330
}
332331
// depth is set to 0, the tree isn't rebuilt so it's not a problem
333-
childN, err := verkle.ParseNode(childS, 0, childC[:])
332+
childN, err := verkle.ParseNode(childS, 0)
334333
if err != nil {
335334
return fmt.Errorf("decode error child %x in db: %w", child.Commitment().Bytes(), err)
336335
}
@@ -390,7 +389,7 @@ func verifyVerkle(ctx *cli.Context) error {
390389
if err != nil {
391390
return err
392391
}
393-
root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
392+
root, err := verkle.ParseNode(serializedRoot, 0)
394393
if err != nil {
395394
return err
396395
}
@@ -439,7 +438,7 @@ func expandVerkle(ctx *cli.Context) error {
439438
if err != nil {
440439
return err
441440
}
442-
root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
441+
root, err := verkle.ParseNode(serializedRoot, 0)
443442
if err != nil {
444443
return err
445444
}

cmd/utils/cmd.go

+84
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,23 @@ import (
2626
"os"
2727
"os/signal"
2828
"runtime"
29+
"runtime/pprof"
2930
"strings"
3031
"syscall"
3132
"time"
3233

3334
"github.com/ethereum/go-ethereum/common"
3435
"github.com/ethereum/go-ethereum/core"
3536
"github.com/ethereum/go-ethereum/core/rawdb"
37+
"github.com/ethereum/go-ethereum/core/state/snapshot"
3638
"github.com/ethereum/go-ethereum/core/types"
3739
"github.com/ethereum/go-ethereum/crypto"
3840
"github.com/ethereum/go-ethereum/eth/ethconfig"
3941
"github.com/ethereum/go-ethereum/ethdb"
4042
"github.com/ethereum/go-ethereum/internal/debug"
4143
"github.com/ethereum/go-ethereum/log"
4244
"github.com/ethereum/go-ethereum/node"
45+
"github.com/ethereum/go-ethereum/params"
4346
"github.com/ethereum/go-ethereum/rlp"
4447
"github.com/urfave/cli/v2"
4548
)
@@ -173,6 +176,18 @@ func ImportChain(chain *core.BlockChain, fn string) error {
173176
return err
174177
}
175178
}
179+
cpuProfile, err := os.Create("cpu.out")
180+
if err != nil {
181+
return fmt.Errorf("Error creating CPU profile: %v", err)
182+
}
183+
defer cpuProfile.Close()
184+
err = pprof.StartCPUProfile(cpuProfile)
185+
if err != nil {
186+
return fmt.Errorf("Error starting CPU profile: %v", err)
187+
}
188+
defer pprof.StopCPUProfile()
189+
params.ClearVerkleWitnessCosts()
190+
176191
stream := rlp.NewStream(reader, 0)
177192

178193
// Run actual the import.
@@ -365,6 +380,75 @@ func ExportPreimages(db ethdb.Database, fn string) error {
365380
return nil
366381
}
367382

383+
// ExportOverlayPreimages exports all known hash preimages into the specified file,
384+
// in the same order as expected by the overlay tree migration.
385+
func ExportOverlayPreimages(chain *core.BlockChain, fn string, root common.Hash) error {
386+
log.Info("Exporting preimages", "file", fn)
387+
388+
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
389+
if err != nil {
390+
return err
391+
}
392+
defer fh.Close()
393+
394+
writer := bufio.NewWriter(fh)
395+
defer writer.Flush()
396+
397+
statedb, err := chain.State()
398+
if err != nil {
399+
return fmt.Errorf("failed to open statedb: %w", err)
400+
}
401+
402+
if root == (common.Hash{}) {
403+
root = chain.CurrentBlock().Root()
404+
}
405+
406+
accIt, err := statedb.Snaps().AccountIterator(root, common.Hash{})
407+
if err != nil {
408+
return err
409+
}
410+
defer accIt.Release()
411+
412+
count := 0
413+
for accIt.Next() {
414+
acc, err := snapshot.FullAccount(accIt.Account())
415+
if err != nil {
416+
return fmt.Errorf("invalid account encountered during traversal: %s", err)
417+
}
418+
addr := rawdb.ReadPreimage(statedb.Database().DiskDB(), accIt.Hash())
419+
if len(addr) != 20 {
420+
return fmt.Errorf("addr len is zero is not 32: %d", len(addr))
421+
}
422+
if _, err := writer.Write(addr); err != nil {
423+
return fmt.Errorf("failed to write addr preimage: %w", err)
424+
}
425+
426+
if acc.HasStorage() {
427+
stIt, err := statedb.Snaps().StorageIterator(root, accIt.Hash(), common.Hash{})
428+
if err != nil {
429+
return fmt.Errorf("failed to create storage iterator: %w", err)
430+
}
431+
for stIt.Next() {
432+
slotnr := rawdb.ReadPreimage(statedb.Database().DiskDB(), stIt.Hash())
433+
if len(slotnr) != 32 {
434+
return fmt.Errorf("slotnr not 32 len")
435+
}
436+
if _, err := writer.Write(slotnr); err != nil {
437+
return fmt.Errorf("failed to write slotnr preimage: %w", err)
438+
}
439+
}
440+
stIt.Release()
441+
}
442+
count++
443+
if count%100000 == 0 {
444+
log.Info("Last exported account", "account", accIt.Hash())
445+
}
446+
}
447+
448+
log.Info("Exported preimages", "file", fn)
449+
return nil
450+
}
451+
368452
// exportHeader is used in the export/import flow. When we do an export,
369453
// the first element we output is the exportHeader.
370454
// Whenever a backwards-incompatible change is made, the Version header

cmd/utils/flags.go

+5
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ var (
219219
Usage: "Max number of elements (0 = no limit)",
220220
Value: 0,
221221
}
222+
TreeRootFlag = &cli.StringFlag{
223+
Name: "roothash",
224+
Usage: "Root hash of the tree (if empty, use the latest)",
225+
Value: "",
226+
}
222227

223228
defaultSyncMode = ethconfig.Defaults.SyncMode
224229
SyncModeFlag = &flags.TextMarshalerFlag{

core/block_validator.go

+18-13
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,17 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
6565
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
6666
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
6767
}
68-
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
69-
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
70-
return consensus.ErrUnknownAncestor
71-
}
72-
return consensus.ErrPrunedAncestor
73-
}
68+
// XXX I had to deactivate this check for replay to work: the block state root
69+
// hash is the one of the overlay tree, but in replay mode, it's the hash of
70+
// the base tree that takes precedence, as the chain would not otherwise be
71+
// recognized.
72+
// if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
73+
// if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
74+
// return consensus.ErrUnknownAncestor
75+
// }
76+
// fmt.Println("failure here")
77+
// return consensus.ErrPrunedAncestor
78+
// }
7479
return nil
7580
}
7681

@@ -90,15 +95,15 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
9095
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
9196
}
9297
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
93-
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
94-
if receiptSha != header.ReceiptHash {
95-
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
96-
}
98+
// receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
99+
// if receiptSha != header.ReceiptHash {
100+
// return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
101+
// }
97102
// Validate the state root against the received state root and throw
98103
// an error if they don't match.
99-
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
100-
return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
101-
}
104+
// if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
105+
// return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
106+
// }
102107
return nil
103108
}
104109

core/blockchain.go

+55
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818
package core
1919

2020
import (
21+
"bufio"
2122
"errors"
2223
"fmt"
2324
"io"
25+
"math"
2426
"math/big"
27+
"os"
2528
"runtime"
2629
"sort"
30+
"strconv"
2731
"strings"
2832
"sync"
2933
"sync/atomic"
@@ -1472,6 +1476,30 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
14721476
return bc.insertChain(chain, true, true)
14731477
}
14741478

1479+
func findVerkleConversionBlock() (uint64, error) {
1480+
if _, err := os.Stat("conversion.txt"); os.IsNotExist(err) {
1481+
return math.MaxUint64, nil
1482+
}
1483+
1484+
f, err := os.Open("conversion.txt")
1485+
if err != nil {
1486+
log.Error("Failed to open conversion.txt", "err", err)
1487+
return 0, err
1488+
}
1489+
defer f.Close()
1490+
1491+
scanner := bufio.NewScanner(f)
1492+
scanner.Scan()
1493+
conversionBlock, err := strconv.ParseUint(scanner.Text(), 10, 64)
1494+
if err != nil {
1495+
log.Error("Failed to parse conversionBlock", "err", err)
1496+
return 0, err
1497+
}
1498+
log.Info("Found conversion block info", "conversionBlock", conversionBlock)
1499+
1500+
return conversionBlock, nil
1501+
}
1502+
14751503
// insertChain is the internal implementation of InsertChain, which assumes that
14761504
// 1) chains are contiguous, and 2) The chain mutex is held.
14771505
//
@@ -1486,6 +1514,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
14861514
return 0, nil
14871515
}
14881516

1517+
conversionBlock, err := findVerkleConversionBlock()
1518+
if err != nil {
1519+
return 0, err
1520+
}
1521+
14891522
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
14901523
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
14911524

@@ -1670,6 +1703,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
16701703
if parent == nil {
16711704
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
16721705
}
1706+
1707+
if parent.Number.Uint64() == conversionBlock {
1708+
bc.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), parent.Number)
1709+
}
16731710
statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
16741711
if err != nil {
16751712
return it.index, err
@@ -1706,6 +1743,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
17061743
return it.index, err
17071744
}
17081745

1746+
if statedb.Database().InTransition() || statedb.Database().Transitioned() {
1747+
bc.AddRootTranslation(block.Root(), statedb.IntermediateRoot(false))
1748+
}
1749+
17091750
// Update the metrics touched during block processing
17101751
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
17111752
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them
@@ -2287,6 +2328,8 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
22872328
return false
22882329
}
22892330

2331+
var emptyVerkleRoot common.Hash
2332+
22902333
// indexBlocks reindexes or unindexes transactions depending on user configuration
22912334
func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{}) {
22922335
defer func() { close(done) }()
@@ -2431,3 +2474,15 @@ func (bc *BlockChain) SetBlockValidatorAndProcessorForTesting(v Validator, p Pro
24312474
bc.validator = v
24322475
bc.processor = p
24332476
}
2477+
2478+
func (bc *BlockChain) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, cancunBlock *big.Int) {
2479+
bc.stateCache.StartVerkleTransition(originalRoot, translatedRoot, chainConfig, cancunBlock)
2480+
}
2481+
2482+
func (bc *BlockChain) EndVerkleTransition() {
2483+
bc.stateCache.EndVerkleTransition()
2484+
}
2485+
2486+
func (bc *BlockChain) AddRootTranslation(originalRoot, translatedRoot common.Hash) {
2487+
bc.stateCache.AddRootTranslation(originalRoot, translatedRoot)
2488+
}

0 commit comments

Comments
 (0)