From ad3281f4e7ad08b5d40acec2b7c7bc44758aa2fe Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Wed, 8 Nov 2023 10:51:02 +0100
Subject: [PATCH 01/22] post-genesis transition
---
core/blockchain.go | 6 +--
core/overlay_transition.go | 42 +++++++++---------
core/state/database.go | 89 ++++++++++++++++++++++----------------
core/state/statedb.go | 9 ++--
core/state_processor.go | 5 ++-
eth/catalyst/api.go | 2 +-
light/trie.go | 24 +++++-----
miner/worker.go | 4 +-
8 files changed, 98 insertions(+), 83 deletions(-)
diff --git a/core/blockchain.go b/core/blockchain.go
index a97bfb20c1aa..ea521835a740 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -1747,7 +1747,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
}
if parent.Number.Uint64() == conversionBlock {
- bc.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), &parent.Time)
+ bc.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), &parent.Time, parent.Root)
bc.stateCache.SetLastMerkleRoot(parent.Root)
}
statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
@@ -2533,8 +2533,8 @@ func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load())
}
-func (bc *BlockChain) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64) {
- bc.stateCache.StartVerkleTransition(originalRoot, translatedRoot, chainConfig, pragueTime)
+func (bc *BlockChain) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64, root common.Hash) {
+ bc.stateCache.StartVerkleTransition(originalRoot, translatedRoot, chainConfig, pragueTime, root)
}
func (bc *BlockChain) ReorgThroughVerkleTransition() {
bc.stateCache.ReorgThroughVerkleTransition()
diff --git a/core/overlay_transition.go b/core/overlay_transition.go
index 35c09d22d938..24bb7d5e6c02 100644
--- a/core/overlay_transition.go
+++ b/core/overlay_transition.go
@@ -35,7 +35,7 @@ import (
)
// OverlayVerkleTransition contains the overlay conversion logic
-func OverlayVerkleTransition(statedb *state.StateDB) error {
+func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
migrdb := statedb.Database()
// verkle transition: if the conversion process is in progress, move
@@ -47,7 +47,7 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
mpt = tt.Base()
vkt = tt.Overlay()
hasPreimagesBin = false
- preimageSeek = migrdb.GetCurrentPreimageOffset()
+ preimageSeek = migrdb.GetCurrentPreimageOffset(root)
fpreimages *bufio.Reader
)
@@ -65,7 +65,7 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
hasPreimagesBin = true
}
- accIt, err := statedb.Snaps().AccountIterator(mpt.Hash(), migrdb.GetCurrentAccountHash())
+ accIt, err := statedb.Snaps().AccountIterator(mpt.Hash(), migrdb.GetCurrentAccountHash(root))
if err != nil {
return err
}
@@ -73,7 +73,7 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
accIt.Next()
// If we're about to start with the migration process, we have to read the first account hash preimage.
- if migrdb.GetCurrentAccountAddress() == nil {
+ if migrdb.GetCurrentAccountAddress(root) == nil {
var addr common.Address
if hasPreimagesBin {
if _, err := io.ReadFull(fpreimages, addr[:]); err != nil {
@@ -85,8 +85,8 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
return fmt.Errorf("addr len is zero is not 32: %d", len(addr))
}
}
- migrdb.SetCurrentAccountAddress(addr)
- if migrdb.GetCurrentAccountHash() != accIt.Hash() {
+ migrdb.SetCurrentAccountAddress(addr, root)
+ if migrdb.GetCurrentAccountHash(root) != accIt.Hash() {
return fmt.Errorf("preimage file does not match account hash: %s != %s", crypto.Keccak256Hash(addr[:]), accIt.Hash())
}
preimageSeek += int64(len(addr))
@@ -108,7 +108,7 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
log.Error("Invalid account encountered during traversal", "error", err)
return err
}
- vkt.SetStorageRootConversion(*migrdb.GetCurrentAccountAddress(), acc.Root)
+ vkt.SetStorageRootConversion(*migrdb.GetCurrentAccountAddress(root), acc.Root)
// Start with processing the storage, because once the account is
// converted, the `stateRoot` field loses its meaning. Which means
@@ -120,7 +120,7 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
// to during normal block execution. A mitigation strategy has been
// introduced with the `*StorageRootConversion` fields in VerkleDB.
if acc.HasStorage() {
- stIt, err := statedb.Snaps().StorageIterator(mpt.Hash(), accIt.Hash(), migrdb.GetCurrentSlotHash())
+ stIt, err := statedb.Snaps().StorageIterator(mpt.Hash(), accIt.Hash(), migrdb.GetCurrentSlotHash(root))
if err != nil {
return err
}
@@ -132,7 +132,7 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
// processing the storage for that account where we left off.
// If the entire storage was processed, then the iterator was
// created in vain, but it's ok as this will not happen often.
- for ; !migrdb.GetStorageProcessed() && count < maxMovedCount; count++ {
+ for ; !migrdb.GetStorageProcessed(root) && count < maxMovedCount; count++ {
var (
value []byte // slot value after RLP decoding
safeValue [32]byte // 32-byte aligned value
@@ -160,12 +160,12 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
}
preimageSeek += int64(len(slotnr))
- mkv.addStorageSlot(migrdb.GetCurrentAccountAddress().Bytes(), slotnr, safeValue[:])
+ mkv.addStorageSlot(migrdb.GetCurrentAccountAddress(root).Bytes(), slotnr, safeValue[:])
// advance the storage iterator
- migrdb.SetStorageProcessed(!stIt.Next())
- if !migrdb.GetStorageProcessed() {
- migrdb.SetCurrentSlotHash(stIt.Hash())
+ migrdb.SetStorageProcessed(!stIt.Next(), root)
+ if !migrdb.GetStorageProcessed(root) {
+ migrdb.SetCurrentSlotHash(stIt.Hash(), root)
}
}
stIt.Release()
@@ -178,20 +178,20 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
if count < maxMovedCount {
count++ // count increase for the account itself
- mkv.addAccount(migrdb.GetCurrentAccountAddress().Bytes(), acc)
- vkt.ClearStrorageRootConversion(*migrdb.GetCurrentAccountAddress())
+ mkv.addAccount(migrdb.GetCurrentAccountAddress(root).Bytes(), acc)
+ vkt.ClearStrorageRootConversion(*migrdb.GetCurrentAccountAddress(root))
// Store the account code if present
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) {
code := rawdb.ReadCode(statedb.Database().DiskDB(), common.BytesToHash(acc.CodeHash))
chunks := trie.ChunkifyCode(code)
- mkv.addAccountCode(migrdb.GetCurrentAccountAddress().Bytes(), uint64(len(code)), chunks)
+ mkv.addAccountCode(migrdb.GetCurrentAccountAddress(root).Bytes(), uint64(len(code)), chunks)
}
// reset storage iterator marker for next account
- migrdb.SetStorageProcessed(false)
- migrdb.SetCurrentSlotHash(common.Hash{})
+ migrdb.SetStorageProcessed(false, root)
+ migrdb.SetCurrentSlotHash(common.Hash{}, root)
// Move to the next account, if available - or end
// the transition otherwise.
@@ -212,7 +212,7 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
return fmt.Errorf("preimage file does not match account hash: %s != %s", crypto.Keccak256Hash(addr[:]), accIt.Hash())
}
preimageSeek += int64(len(addr))
- migrdb.SetCurrentAccountAddress(addr)
+ migrdb.SetCurrentAccountAddress(addr, root)
} else {
// case when the account iterator has
// reached the end but count < maxCount
@@ -221,9 +221,9 @@ func OverlayVerkleTransition(statedb *state.StateDB) error {
}
}
}
- migrdb.SetCurrentPreimageOffset(preimageSeek)
+ migrdb.SetCurrentPreimageOffset(preimageSeek, root)
- log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account", statedb.Database().GetCurrentAccountHash())
+ log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account", statedb.Database().GetCurrentAccountHash(root))
// Take all the collected key-values and prepare the new leaf values.
// This fires a background routine that will start doing the work that
diff --git a/core/state/database.go b/core/state/database.go
index 5707e2c88b60..7c42cb9cde5b 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -64,7 +64,7 @@ type Database interface {
// TrieDB retrieves the low level trie database used for data storage.
TrieDB() *trie.Database
- StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, cancunTime *uint64)
+ StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, cancunTime *uint64, root common.Hash)
ReorgThroughVerkleTransition()
@@ -74,27 +74,27 @@ type Database interface {
Transitioned() bool
- SetCurrentSlotHash(hash common.Hash)
+ SetCurrentSlotHash(common.Hash, common.Hash)
- GetCurrentAccountAddress() *common.Address
+ GetCurrentAccountAddress(common.Hash) *common.Address
- SetCurrentAccountAddress(common.Address)
+ SetCurrentAccountAddress(common.Address, common.Hash)
- GetCurrentAccountHash() common.Hash
+ GetCurrentAccountHash(common.Hash) common.Hash
- GetCurrentSlotHash() common.Hash
+ GetCurrentSlotHash(common.Hash) common.Hash
- SetStorageProcessed(bool)
+ SetStorageProcessed(bool, common.Hash)
- GetStorageProcessed() bool
+ GetStorageProcessed(common.Hash) bool
- GetCurrentPreimageOffset() int64
+ GetCurrentPreimageOffset(common.Hash) int64
- SetCurrentPreimageOffset(int64)
+ SetCurrentPreimageOffset(int64, common.Hash)
AddRootTranslation(originalRoot, translatedRoot common.Hash)
- SetLastMerkleRoot(root common.Hash)
+ SetLastMerkleRoot(common.Hash)
}
// Trie is a Ethereum Merkle Patricia trie.
@@ -187,6 +187,10 @@ func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
triedb: trie.NewDatabaseWithConfig(db, config),
addrToPoint: utils.NewPointCache(),
+ StorageProcessed: map[common.Hash]bool{},
+ CurrentAccountAddress: map[common.Hash]*common.Address{},
+ CurrentSlotHash: map[common.Hash]common.Hash{},
+ CurrentPreimageOffset: map[common.Hash]int64{},
}
}
@@ -199,6 +203,10 @@ func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
triedb: triedb,
addrToPoint: utils.NewPointCache(),
ended: triedb.IsVerkle(),
+ StorageProcessed: map[common.Hash]bool{},
+ CurrentAccountAddress: map[common.Hash]*common.Address{},
+ CurrentSlotHash: map[common.Hash]common.Hash{},
+ CurrentPreimageOffset: map[common.Hash]int64{},
}
}
@@ -211,7 +219,7 @@ func (db *cachingDB) Transitioned() bool {
}
// Fork implements the fork
-func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64) {
+func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64, root common.Hash) {
fmt.Println(`
__________.__ .__ .__ __ .__ .__ ____
\__ ___| |__ ____ ____ | | ____ ______ | |__ _____ _____/ |_ | |__ _____ ______ __ _ _|__| ____ / ___\ ______
@@ -224,7 +232,12 @@ func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.H
// db.AddTranslation(originalRoot, translatedRoot)
db.baseRoot = originalRoot
// initialize so that the first storage-less accounts are processed
- db.StorageProcessed = true
+ db.StorageProcessed[root] = true
+
+ // Reinitialize values in case of a reorg
+ db.CurrentAccountAddress[root] = &(common.Address{})
+ db.CurrentSlotHash[root] = common.Hash{}
+ db.CurrentPreimageOffset[root] = 0
if pragueTime != nil {
chainConfig.PragueTime = pragueTime
}
@@ -263,14 +276,14 @@ type cachingDB struct {
addrToPoint *utils.PointCache
baseRoot common.Hash // hash of the read-only base tree
- CurrentAccountAddress *common.Address // addresss of the last translated account
- CurrentSlotHash common.Hash // hash of the last translated storage slot
- CurrentPreimageOffset int64 // next byte to read from the preimage file
+ CurrentAccountAddress map[common.Hash]*common.Address // addresss of the last translated account
+ CurrentSlotHash map[common.Hash]common.Hash // hash of the last translated storage slot
+ CurrentPreimageOffset map[common.Hash]int64 // next byte to read from the preimage file
// Mark whether the storage for an account has been processed. This is useful if the
// maximum number of leaves of the conversion is reached before the whole storage is
// processed.
- StorageProcessed bool
+ StorageProcessed map[common.Hash]bool
}
func (db *cachingDB) openMPTTrie(root common.Hash) (Trie, error) {
@@ -450,49 +463,49 @@ func (db *cachingDB) GetTreeKeyHeader(addr []byte) *verkle.Point {
return db.addrToPoint.GetTreeKeyHeader(addr)
}
-func (db *cachingDB) SetCurrentAccountAddress(addr common.Address) {
- db.CurrentAccountAddress = &addr
+func (db *cachingDB) SetCurrentAccountAddress(addr common.Address, root common.Hash) {
+ db.CurrentAccountAddress[root] = &addr
}
-func (db *cachingDB) GetCurrentAccountHash() common.Hash {
+func (db *cachingDB) GetCurrentAccountHash(root common.Hash) common.Hash {
var addrHash common.Hash
- if db.CurrentAccountAddress != nil {
- addrHash = crypto.Keccak256Hash(db.CurrentAccountAddress[:])
+ if db.CurrentAccountAddress[root] != nil {
+ addrHash = crypto.Keccak256Hash(db.CurrentAccountAddress[root][:])
}
return addrHash
}
-func (db *cachingDB) GetCurrentAccountAddress() *common.Address {
- return db.CurrentAccountAddress
+func (db *cachingDB) GetCurrentAccountAddress(root common.Hash) *common.Address {
+ return db.CurrentAccountAddress[root]
}
-func (db *cachingDB) GetCurrentPreimageOffset() int64 {
- return db.CurrentPreimageOffset
+func (db *cachingDB) GetCurrentPreimageOffset(root common.Hash) int64 {
+ return db.CurrentPreimageOffset[root]
}
-func (db *cachingDB) SetCurrentPreimageOffset(offset int64) {
- db.CurrentPreimageOffset = offset
+func (db *cachingDB) SetCurrentPreimageOffset(offset int64, root common.Hash) {
+ db.CurrentPreimageOffset[root] = offset
}
-func (db *cachingDB) SetCurrentSlotHash(hash common.Hash) {
- db.CurrentSlotHash = hash
+func (db *cachingDB) SetCurrentSlotHash(hash common.Hash, root common.Hash) {
+ db.CurrentSlotHash[root] = hash
}
-func (db *cachingDB) GetCurrentSlotHash() common.Hash {
- return db.CurrentSlotHash
+func (db *cachingDB) GetCurrentSlotHash(root common.Hash) common.Hash {
+ return db.CurrentSlotHash[root]
}
-func (db *cachingDB) SetStorageProcessed(processed bool) {
- db.StorageProcessed = processed
+func (db *cachingDB) SetStorageProcessed(processed bool, root common.Hash) {
+ db.StorageProcessed[root] = processed
}
-func (db *cachingDB) GetStorageProcessed() bool {
- return db.StorageProcessed
+func (db *cachingDB) GetStorageProcessed(root common.Hash) bool {
+ return db.StorageProcessed[root]
}
func (db *cachingDB) AddRootTranslation(originalRoot, translatedRoot common.Hash) {
}
-func (db *cachingDB) SetLastMerkleRoot(root common.Hash) {
- db.LastMerkleRoot = root
+func (db *cachingDB) SetLastMerkleRoot(merkleRoot common.Hash) {
+ db.LastMerkleRoot = merkleRoot
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 1b47746458b5..a10c5252f113 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -176,10 +176,11 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
if tr.IsVerkle() {
sdb.witness = sdb.NewAccessWitness()
}
- // if sdb.snaps != nil {
- // if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap == nil {
- // }
- // }
+ if sdb.snaps != nil {
+ // if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap == nil {
+ // }
+ sdb.snap = sdb.snaps.Snapshot(root)
+ }
return sdb, nil
}
diff --git a/core/state_processor.go b/core/state_processor.go
index d28df7aac1d0..8cc8ac50ca3d 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -115,7 +115,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
}
// Perform the overlay transition, if relevant
- if err := OverlayVerkleTransition(statedb); err != nil {
+ parent := p.bc.GetHeaderByHash(header.ParentHash)
+ if err := OverlayVerkleTransition(statedb, parent.Root); err != nil {
return nil, nil, 0, fmt.Errorf("error performing verkle overlay transition: %w", err)
}
@@ -329,7 +330,7 @@ func (kvm *keyValueMigrator) prepare() {
var currAddr common.Address
var currPoint *verkle.Point
for i := range batch {
- if batch[i].branchKey.addr != currAddr {
+ if batch[i].branchKey.addr != currAddr || currAddr == (common.Address{}) {
currAddr = batch[i].branchKey.addr
currPoint = tutils.EvaluateAddressPoint(currAddr[:])
}
diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go
index 63079415fc14..864b3efe84f5 100644
--- a/eth/catalyst/api.go
+++ b/eth/catalyst/api.go
@@ -532,7 +532,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
if api.eth.BlockChain().Config().IsPrague(block.Number(), block.Time()) && !api.eth.BlockChain().Config().IsPrague(parent.Number(), parent.Time()) {
parent := api.eth.BlockChain().GetHeaderByNumber(block.NumberU64() - 1)
if !api.eth.BlockChain().Config().IsPrague(parent.Number, parent.Time) {
- api.eth.BlockChain().StartVerkleTransition(parent.Root, common.Hash{}, api.eth.BlockChain().Config(), nil)
+ api.eth.BlockChain().StartVerkleTransition(parent.Root, common.Hash{}, api.eth.BlockChain().Config(), nil, parent.Root)
}
}
// Reset db merge state in case of a reorg
diff --git a/light/trie.go b/light/trie.go
index 53d54615d909..6d0c654ff111 100644
--- a/light/trie.go
+++ b/light/trie.go
@@ -101,7 +101,7 @@ func (db *odrDatabase) DiskDB() ethdb.KeyValueStore {
panic("not implemented")
}
-func (db *odrDatabase) StartVerkleTransition(originalRoot common.Hash, translatedRoot common.Hash, chainConfig *params.ChainConfig, _ *uint64) {
+func (db *odrDatabase) StartVerkleTransition(originalRoot common.Hash, translatedRoot common.Hash, chainConfig *params.ChainConfig, _ *uint64, _ common.Hash) {
panic("not implemented") // TODO: Implement
}
@@ -121,47 +121,47 @@ func (db *odrDatabase) Transitioned() bool {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetCurrentSlotHash(hash common.Hash) {
+func (db *odrDatabase) SetCurrentSlotHash(common.Hash, common.Hash) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetCurrentAccountAddress() *common.Address {
+func (db *odrDatabase) GetCurrentAccountAddress(common.Hash) *common.Address {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetCurrentAccountAddress(_ common.Address) {
+func (db *odrDatabase) SetCurrentAccountAddress(common.Address, common.Hash) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetCurrentAccountHash() common.Hash {
+func (db *odrDatabase) GetCurrentAccountHash(common.Hash) common.Hash {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetCurrentSlotHash() common.Hash {
+func (db *odrDatabase) GetCurrentSlotHash(common.Hash) common.Hash {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetStorageProcessed(_ bool) {
+func (db *odrDatabase) SetStorageProcessed(bool, common.Hash) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetStorageProcessed() bool {
+func (db *odrDatabase) GetStorageProcessed(common.Hash) bool {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetCurrentPreimageOffset() int64 {
+func (db *odrDatabase) GetCurrentPreimageOffset(common.Hash) int64 {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetCurrentPreimageOffset(_ int64) {
+func (db *odrDatabase) SetCurrentPreimageOffset(int64, common.Hash) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) AddRootTranslation(originalRoot common.Hash, translatedRoot common.Hash) {
+func (db *odrDatabase) AddRootTranslation(common.Hash, common.Hash) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetLastMerkleRoot(root common.Hash) {
+func (db *odrDatabase) SetLastMerkleRoot(common.Hash) {
panic("not implemented") // TODO: Implement
}
diff --git a/miner/worker.go b/miner/worker.go
index aae4fe8b6454..1d1b2fda07a2 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -894,7 +894,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
if w.chain.Config().IsPrague(header.Number, header.Time) {
parent := w.chain.GetHeaderByNumber(header.Number.Uint64() - 1)
if !w.chain.Config().IsPrague(parent.Number, parent.Time) {
- w.chain.StartVerkleTransition(parent.Root, common.Hash{}, w.chain.Config(), nil)
+ w.chain.StartVerkleTransition(parent.Root, common.Hash{}, w.chain.Config(), w.chain.Config().PragueTime, parent.Root)
}
}
@@ -905,7 +905,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
return nil, err
}
if w.chain.Config().IsPrague(header.Number, header.Time) {
- core.OverlayVerkleTransition(state)
+ core.OverlayVerkleTransition(state, parent.Root)
}
// Run the consensus preparation with the default or customized consensus engine.
if err := w.engine.Prepare(w.chain, header); err != nil {
From b7381c0240c46156060c7f5704af5ac24af4df9d Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Wed, 8 Nov 2023 11:05:08 +0100
Subject: [PATCH 02/22] quell linter issue
---
core/state/database.go | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/core/state/database.go b/core/state/database.go
index 7c42cb9cde5b..cd9f8dcf43f1 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -182,11 +182,11 @@ func NewDatabase(db ethdb.Database) Database {
// large memory cache.
func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
return &cachingDB{
- disk: db,
- codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
- codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
- triedb: trie.NewDatabaseWithConfig(db, config),
- addrToPoint: utils.NewPointCache(),
+ disk: db,
+ codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
+ codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
+ triedb: trie.NewDatabaseWithConfig(db, config),
+ addrToPoint: utils.NewPointCache(),
StorageProcessed: map[common.Hash]bool{},
CurrentAccountAddress: map[common.Hash]*common.Address{},
CurrentSlotHash: map[common.Hash]common.Hash{},
@@ -197,12 +197,12 @@ func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
return &cachingDB{
- disk: db,
- codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
- codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
- triedb: triedb,
- addrToPoint: utils.NewPointCache(),
- ended: triedb.IsVerkle(),
+ disk: db,
+ codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
+ codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
+ triedb: triedb,
+ addrToPoint: utils.NewPointCache(),
+ ended: triedb.IsVerkle(),
StorageProcessed: map[common.Hash]bool{},
CurrentAccountAddress: map[common.Hash]*common.Address{},
CurrentSlotHash: map[common.Hash]common.Hash{},
@@ -275,7 +275,7 @@ type cachingDB struct {
addrToPoint *utils.PointCache
- baseRoot common.Hash // hash of the read-only base tree
+ baseRoot common.Hash // hash of the read-only base tree
CurrentAccountAddress map[common.Hash]*common.Address // addresss of the last translated account
CurrentSlotHash map[common.Hash]common.Hash // hash of the last translated storage slot
CurrentPreimageOffset map[common.Hash]int64 // next byte to read from the preimage file
From d49cbd6331ffbac89fc55a7a63ff181d859ed3a0 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Wed, 8 Nov 2023 17:33:28 +0100
Subject: [PATCH 03/22] support Transition post tree in conversion
---
consensus/beacon/consensus.go | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go
index 35a7ed2b56d0..5eeddc47f07e 100644
--- a/consensus/beacon/consensus.go
+++ b/consensus/beacon/consensus.go
@@ -410,7 +410,19 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
switch pre := preTrie.(type) {
case *trie.VerkleTrie:
vtrpre, okpre = preTrie.(*trie.VerkleTrie)
- vtrpost, okpost = state.GetTrie().(*trie.VerkleTrie)
+ switch tr := state.GetTrie().(type) {
+ case *trie.VerkleTrie:
+ vtrpost = tr
+ okpost = true
+ // This is to handle a situation right at the start of the conversion:
+ // the post trie is a transition tree when the pre tree is an empty
+ // verkle tree.
+ case *trie.TransitionTrie:
+ vtrpost = tr.Overlay()
+ okpost = true
+ default:
+ okpost = false
+ }
case *trie.TransitionTrie:
vtrpre = pre.Overlay()
okpre = true
From a7bc86f6dcca1ee9e2e18f04985225fdb8aa5a75 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 30 Nov 2023 14:45:58 +0100
Subject: [PATCH 04/22] Refactor transition post genesis (#311)
* rewrite per-block conversion pointer management
* remove unused method
* fix: a branch that can verge at genesis or post genesis (#314)
* fix: import cycle in conversion refactor (#315)
* fix shadowfork panic in OpenStorageTrie
---
consensus/beacon/consensus.go | 7 +
core/blockchain.go | 6 +
core/chain_makers.go | 7 +
core/genesis.go | 21 +-
.../conversion.go} | 224 ++++++++++++++++--
core/state/database.go | 206 ++++++++++------
core/state_processor.go | 200 +---------------
light/trie.go | 30 ++-
miner/worker.go | 3 -
9 files changed, 389 insertions(+), 315 deletions(-)
rename core/{overlay_transition.go => overlay/conversion.go} (53%)
diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go
index 5eeddc47f07e..dd94b8da7790 100644
--- a/consensus/beacon/consensus.go
+++ b/consensus/beacon/consensus.go
@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
+ "github.com/ethereum/go-ethereum/core/overlay"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
@@ -363,6 +364,12 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.CodeKeccakLeafKey)
state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.CodeSizeLeafKey)
}
+
+ if chain.Config().IsPrague(header.Number, header.Time) {
+ fmt.Println("at block", header.Number, "performing transition?", state.Database().InTransition())
+ parent := chain.GetHeaderByHash(header.ParentHash)
+ overlay.OverlayVerkleTransition(state, parent.Root)
+ }
}
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
diff --git a/core/blockchain.go b/core/blockchain.go
index ea521835a740..6dcd0e81fab3 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -313,6 +313,12 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
// Declare the end of the verkle transition if need be
if bc.chainConfig.Rules(head.Number, false /* XXX */, head.Time).IsPrague {
+ // TODO this only works when resuming a chain that has already gone
+ // through the conversion. All pointers should be saved to the DB
+ // for it to be able to recover if interrupted during the transition
+ // but that's left out to a later PR since there's not really a need
+ // right now.
+ bc.stateCache.InitTransitionStatus(true, true)
bc.stateCache.EndVerkleTransition()
}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 5b9dc0c6ff08..3909500d91fa 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -435,8 +435,15 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
}
var snaps *snapshot.Tree
triedb := state.NewDatabaseWithConfig(db, nil)
+ triedb.StartVerkleTransition(common.Hash{}, common.Hash{}, config, config.PragueTime, common.Hash{})
triedb.EndVerkleTransition()
+ //statedb, err := state.New(parent.Root(), triedb, snaps)
+ //if err != nil {
+ // panic(fmt.Sprintf("could not find state for block %d: err=%v, parent root=%x", parent.NumberU64(), err, parent.Root()))
+ //}
+ statedb.Database().SaveTransitionState(parent.Root())
for i := 0; i < n; i++ {
+ // XXX merge uncommment
statedb, err := state.New(parent.Root(), triedb, snaps)
if err != nil {
panic(fmt.Sprintf("could not find state for block %d: err=%v, parent root=%x", i, err, parent.Root()))
diff --git a/core/genesis.go b/core/genesis.go
index c8a4bc5952d9..0aad87a10d70 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -126,6 +126,7 @@ func (ga *GenesisAlloc) deriveHash(cfg *params.ChainConfig, timestamp uint64) (c
// all the derived states will be discarded to not pollute disk.
db := state.NewDatabase(rawdb.NewMemoryDatabase())
if cfg.IsPrague(big.NewInt(int64(0)), timestamp) {
+ db.StartVerkleTransition(common.Hash{}, common.Hash{}, cfg, ×tamp, common.Hash{})
db.EndVerkleTransition()
}
statedb, err := state.New(types.EmptyRootHash, db, nil)
@@ -146,15 +147,17 @@ func (ga *GenesisAlloc) deriveHash(cfg *params.ChainConfig, timestamp uint64) (c
// flush is very similar with deriveHash, but the main difference is
// all the generated states will be persisted into the given database.
// Also, the genesis state specification will be flushed as well.
-func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhash common.Hash, cfg *params.ChainConfig) error {
- statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
- if err != nil {
- return err
+func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhash common.Hash, cfg *params.ChainConfig, timestamp *uint64) error {
+ database := state.NewDatabaseWithNodeDB(db, triedb)
+ // End the verkle conversion at genesis if the fork block is 0
+ if timestamp != nil && cfg.IsPrague(big.NewInt(int64(0)), *timestamp) {
+ database.StartVerkleTransition(common.Hash{}, common.Hash{}, cfg, timestamp, common.Hash{})
+ database.EndVerkleTransition()
}
- // End the verkle conversion at genesis if the fork block is 0
- if triedb.IsVerkle() {
- statedb.Database().EndVerkleTransition()
+ statedb, err := state.New(types.EmptyRootHash, database, nil)
+ if err != nil {
+ return err
}
for addr, account := range *ga {
@@ -221,7 +224,7 @@ func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash comm
return errors.New("not found")
}
}
- return alloc.flush(db, triedb, blockhash, config)
+ return alloc.flush(db, triedb, blockhash, config, nil)
}
// GenesisAccount is an account in the state of the genesis block.
@@ -536,7 +539,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block
// All the checks has passed, flush the states derived from the genesis
// specification as well as the specification itself into the provided
// database.
- if err := g.Alloc.flush(db, triedb, block.Hash(), g.Config); err != nil {
+ if err := g.Alloc.flush(db, triedb, block.Hash(), g.Config, &g.Timestamp); err != nil {
return nil, err
}
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
diff --git a/core/overlay_transition.go b/core/overlay/conversion.go
similarity index 53%
rename from core/overlay_transition.go
rename to core/overlay/conversion.go
index 24bb7d5e6c02..e76aa5900173 100644
--- a/core/overlay_transition.go
+++ b/core/overlay/conversion.go
@@ -14,14 +14,17 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package overlay
import (
"bufio"
"bytes"
+ "encoding/binary"
"fmt"
"io"
"os"
+ "runtime"
+ "sync"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -32,8 +35,187 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/trie/utils"
+ "github.com/gballet/go-verkle"
+ "github.com/holiman/uint256"
)
+var zeroTreeIndex uint256.Int
+
+// keyValueMigrator is a helper module that collects key-values from the overlay-tree migration for Verkle Trees.
+// It assumes that the walk of the base tree is done in address-order, so it exploit that fact to
+// collect the key-values in a way that is efficient.
+type keyValueMigrator struct {
+ // leafData contains the values for the future leaf for a particular VKT branch.
+ leafData []migratedKeyValue
+
+ // When prepare() is called, it will start a background routine that will process the leafData
+ // saving the result in newLeaves to be used by migrateCollectedKeyValues(). The background
+ // routine signals that it is done by closing processingReady.
+ processingReady chan struct{}
+ newLeaves []verkle.LeafNode
+ prepareErr error
+}
+
+func newKeyValueMigrator() *keyValueMigrator {
+ // We do initialize the VKT config since prepare() might indirectly make multiple GetConfig() calls
+ // in different goroutines when we never called GetConfig() before, causing a race considering the way
+ // that `config` is designed in go-verkle.
+ // TODO: jsign as a fix for this in the PR where we move to a file-less precomp, since it allows safe
+ // concurrent calls to GetConfig(). When that gets merged, we can remove this line.
+ _ = verkle.GetConfig()
+ return &keyValueMigrator{
+ processingReady: make(chan struct{}),
+ leafData: make([]migratedKeyValue, 0, 10_000),
+ }
+}
+
+type migratedKeyValue struct {
+ branchKey branchKey
+ leafNodeData verkle.BatchNewLeafNodeData
+}
+type branchKey struct {
+ addr common.Address
+ treeIndex uint256.Int
+}
+
+func newBranchKey(addr []byte, treeIndex *uint256.Int) branchKey {
+ var sk branchKey
+ copy(sk.addr[:], addr)
+ sk.treeIndex = *treeIndex
+ return sk
+}
+
+func (kvm *keyValueMigrator) addStorageSlot(addr []byte, slotNumber []byte, slotValue []byte) {
+ treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slotNumber)
+ leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, treeIndex))
+ leafNodeData.Values[subIndex] = slotValue
+}
+
+func (kvm *keyValueMigrator) addAccount(addr []byte, acc *types.StateAccount) {
+ leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, &zeroTreeIndex))
+
+ var version [verkle.LeafValueSize]byte
+ leafNodeData.Values[utils.VersionLeafKey] = version[:]
+
+ var balance [verkle.LeafValueSize]byte
+ for i, b := range acc.Balance.Bytes() {
+ balance[len(acc.Balance.Bytes())-1-i] = b
+ }
+ leafNodeData.Values[utils.BalanceLeafKey] = balance[:]
+
+ var nonce [verkle.LeafValueSize]byte
+ binary.LittleEndian.PutUint64(nonce[:8], acc.Nonce)
+ leafNodeData.Values[utils.NonceLeafKey] = nonce[:]
+
+ leafNodeData.Values[utils.CodeKeccakLeafKey] = acc.CodeHash[:]
+}
+
+func (kvm *keyValueMigrator) addAccountCode(addr []byte, codeSize uint64, chunks []byte) {
+ leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, &zeroTreeIndex))
+
+ // Save the code size.
+ var codeSizeBytes [verkle.LeafValueSize]byte
+ binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize)
+ leafNodeData.Values[utils.CodeSizeLeafKey] = codeSizeBytes[:]
+
+ // The first 128 chunks are stored in the account header leaf.
+ for i := 0; i < 128 && i < len(chunks)/32; i++ {
+ leafNodeData.Values[byte(128+i)] = chunks[32*i : 32*(i+1)]
+ }
+
+ // Potential further chunks, have their own leaf nodes.
+ for i := 128; i < len(chunks)/32; {
+ treeIndex, _ := utils.GetTreeKeyCodeChunkIndices(uint256.NewInt(uint64(i)))
+ leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, treeIndex))
+
+ j := i
+ for ; (j-i) < 256 && j < len(chunks)/32; j++ {
+ leafNodeData.Values[byte((j-128)%256)] = chunks[32*j : 32*(j+1)]
+ }
+ i = j
+ }
+}
+
+func (kvm *keyValueMigrator) getOrInitLeafNodeData(bk branchKey) *verkle.BatchNewLeafNodeData {
+ // Remember that keyValueMigration receives actions ordered by (address, subtreeIndex).
+ // This means that we can assume that the last element of leafData is the one that we
+ // are looking for, or that we need to create a new one.
+ if len(kvm.leafData) == 0 || kvm.leafData[len(kvm.leafData)-1].branchKey != bk {
+ kvm.leafData = append(kvm.leafData, migratedKeyValue{
+ branchKey: bk,
+ leafNodeData: verkle.BatchNewLeafNodeData{
+ Stem: nil, // It will be calculated in the prepare() phase, since it's CPU heavy.
+ Values: make(map[byte][]byte),
+ },
+ })
+ }
+ return &kvm.leafData[len(kvm.leafData)-1].leafNodeData
+}
+
+func (kvm *keyValueMigrator) prepare() {
+ // We fire a background routine to process the leafData and save the result in newLeaves.
+ // The background routine signals that it is done by closing processingReady.
+ go func() {
+ // Step 1: We split kvm.leafData in numBatches batches, and we process each batch in a separate goroutine.
+ // This fills each leafNodeData.Stem with the correct value.
+ var wg sync.WaitGroup
+ batchNum := runtime.NumCPU()
+ batchSize := (len(kvm.leafData) + batchNum - 1) / batchNum
+ for i := 0; i < len(kvm.leafData); i += batchSize {
+ start := i
+ end := i + batchSize
+ if end > len(kvm.leafData) {
+ end = len(kvm.leafData)
+ }
+ wg.Add(1)
+
+ batch := kvm.leafData[start:end]
+ go func() {
+ defer wg.Done()
+ var currAddr common.Address
+ var currPoint *verkle.Point
+ for i := range batch {
+ if batch[i].branchKey.addr != currAddr || currAddr == (common.Address{}) {
+ currAddr = batch[i].branchKey.addr
+ currPoint = utils.EvaluateAddressPoint(currAddr[:])
+ }
+ stem := utils.GetTreeKeyWithEvaluatedAddess(currPoint, &batch[i].branchKey.treeIndex, 0)
+ stem = stem[:verkle.StemSize]
+ batch[i].leafNodeData.Stem = stem
+ }
+ }()
+ }
+ wg.Wait()
+
+ // Step 2: Now that we have all stems (i.e: tree keys) calculated, we can create the new leaves.
+ nodeValues := make([]verkle.BatchNewLeafNodeData, len(kvm.leafData))
+ for i := range kvm.leafData {
+ nodeValues[i] = kvm.leafData[i].leafNodeData
+ }
+
+ // Create all leaves in batch mode so we can optimize cryptography operations.
+ kvm.newLeaves, kvm.prepareErr = verkle.BatchNewLeafNode(nodeValues)
+ close(kvm.processingReady)
+ }()
+}
+
+func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) error {
+ now := time.Now()
+ <-kvm.processingReady
+ if kvm.prepareErr != nil {
+ return fmt.Errorf("failed to prepare key values: %w", kvm.prepareErr)
+ }
+ log.Info("Prepared key values from base tree", "duration", time.Since(now))
+
+ // Insert into the tree.
+ if err := tree.InsertMigratedLeaves(kvm.newLeaves); err != nil {
+ return fmt.Errorf("failed to insert migrated leaves: %w", err)
+ }
+
+ return nil
+}
+
// OverlayVerkleTransition contains the overlay conversion logic
func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
migrdb := statedb.Database()
@@ -47,7 +229,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
mpt = tt.Base()
vkt = tt.Overlay()
hasPreimagesBin = false
- preimageSeek = migrdb.GetCurrentPreimageOffset(root)
+ preimageSeek = migrdb.GetCurrentPreimageOffset()
fpreimages *bufio.Reader
)
@@ -65,7 +247,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
hasPreimagesBin = true
}
- accIt, err := statedb.Snaps().AccountIterator(mpt.Hash(), migrdb.GetCurrentAccountHash(root))
+ accIt, err := statedb.Snaps().AccountIterator(mpt.Hash(), migrdb.GetCurrentAccountHash())
if err != nil {
return err
}
@@ -73,7 +255,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
accIt.Next()
// If we're about to start with the migration process, we have to read the first account hash preimage.
- if migrdb.GetCurrentAccountAddress(root) == nil {
+ if migrdb.GetCurrentAccountAddress() == nil {
var addr common.Address
if hasPreimagesBin {
if _, err := io.ReadFull(fpreimages, addr[:]); err != nil {
@@ -85,8 +267,8 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
return fmt.Errorf("addr len is zero is not 32: %d", len(addr))
}
}
- migrdb.SetCurrentAccountAddress(addr, root)
- if migrdb.GetCurrentAccountHash(root) != accIt.Hash() {
+ migrdb.SetCurrentAccountAddress(addr)
+ if migrdb.GetCurrentAccountHash() != accIt.Hash() {
return fmt.Errorf("preimage file does not match account hash: %s != %s", crypto.Keccak256Hash(addr[:]), accIt.Hash())
}
preimageSeek += int64(len(addr))
@@ -108,7 +290,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
log.Error("Invalid account encountered during traversal", "error", err)
return err
}
- vkt.SetStorageRootConversion(*migrdb.GetCurrentAccountAddress(root), acc.Root)
+ vkt.SetStorageRootConversion(*migrdb.GetCurrentAccountAddress(), acc.Root)
// Start with processing the storage, because once the account is
// converted, the `stateRoot` field loses its meaning. Which means
@@ -120,7 +302,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
// to during normal block execution. A mitigation strategy has been
// introduced with the `*StorageRootConversion` fields in VerkleDB.
if acc.HasStorage() {
- stIt, err := statedb.Snaps().StorageIterator(mpt.Hash(), accIt.Hash(), migrdb.GetCurrentSlotHash(root))
+ stIt, err := statedb.Snaps().StorageIterator(mpt.Hash(), accIt.Hash(), migrdb.GetCurrentSlotHash())
if err != nil {
return err
}
@@ -132,7 +314,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
// processing the storage for that account where we left off.
// If the entire storage was processed, then the iterator was
// created in vain, but it's ok as this will not happen often.
- for ; !migrdb.GetStorageProcessed(root) && count < maxMovedCount; count++ {
+ for ; !migrdb.GetStorageProcessed() && count < maxMovedCount; count++ {
var (
value []byte // slot value after RLP decoding
safeValue [32]byte // 32-byte aligned value
@@ -160,12 +342,12 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
}
preimageSeek += int64(len(slotnr))
- mkv.addStorageSlot(migrdb.GetCurrentAccountAddress(root).Bytes(), slotnr, safeValue[:])
+ mkv.addStorageSlot(migrdb.GetCurrentAccountAddress().Bytes(), slotnr, safeValue[:])
// advance the storage iterator
- migrdb.SetStorageProcessed(!stIt.Next(), root)
- if !migrdb.GetStorageProcessed(root) {
- migrdb.SetCurrentSlotHash(stIt.Hash(), root)
+ migrdb.SetStorageProcessed(!stIt.Next())
+ if !migrdb.GetStorageProcessed() {
+ migrdb.SetCurrentSlotHash(stIt.Hash())
}
}
stIt.Release()
@@ -178,20 +360,20 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
if count < maxMovedCount {
count++ // count increase for the account itself
- mkv.addAccount(migrdb.GetCurrentAccountAddress(root).Bytes(), acc)
- vkt.ClearStrorageRootConversion(*migrdb.GetCurrentAccountAddress(root))
+ mkv.addAccount(migrdb.GetCurrentAccountAddress().Bytes(), acc)
+ vkt.ClearStrorageRootConversion(*migrdb.GetCurrentAccountAddress())
// Store the account code if present
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) {
code := rawdb.ReadCode(statedb.Database().DiskDB(), common.BytesToHash(acc.CodeHash))
chunks := trie.ChunkifyCode(code)
- mkv.addAccountCode(migrdb.GetCurrentAccountAddress(root).Bytes(), uint64(len(code)), chunks)
+ mkv.addAccountCode(migrdb.GetCurrentAccountAddress().Bytes(), uint64(len(code)), chunks)
}
// reset storage iterator marker for next account
- migrdb.SetStorageProcessed(false, root)
- migrdb.SetCurrentSlotHash(common.Hash{}, root)
+ migrdb.SetStorageProcessed(false)
+ migrdb.SetCurrentSlotHash(common.Hash{})
// Move to the next account, if available - or end
// the transition otherwise.
@@ -212,7 +394,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
return fmt.Errorf("preimage file does not match account hash: %s != %s", crypto.Keccak256Hash(addr[:]), accIt.Hash())
}
preimageSeek += int64(len(addr))
- migrdb.SetCurrentAccountAddress(addr, root)
+ migrdb.SetCurrentAccountAddress(addr)
} else {
// case when the account iterator has
// reached the end but count < maxCount
@@ -221,9 +403,9 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
}
}
}
- migrdb.SetCurrentPreimageOffset(preimageSeek, root)
+ migrdb.SetCurrentPreimageOffset(preimageSeek)
- log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account", statedb.Database().GetCurrentAccountHash(root))
+ log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account", statedb.Database().GetCurrentAccountHash())
// Take all the collected key-values and prepare the new leaf values.
// This fires a background routine that will start doing the work that
diff --git a/core/state/database.go b/core/state/database.go
index cd9f8dcf43f1..fcc41ba95927 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
@@ -74,27 +75,33 @@ type Database interface {
Transitioned() bool
- SetCurrentSlotHash(common.Hash, common.Hash)
+ InitTransitionStatus(bool, bool)
- GetCurrentAccountAddress(common.Hash) *common.Address
+ SetCurrentSlotHash(common.Hash)
- SetCurrentAccountAddress(common.Address, common.Hash)
+ GetCurrentAccountAddress() *common.Address
- GetCurrentAccountHash(common.Hash) common.Hash
+ SetCurrentAccountAddress(common.Address)
- GetCurrentSlotHash(common.Hash) common.Hash
+ GetCurrentAccountHash() common.Hash
- SetStorageProcessed(bool, common.Hash)
+ GetCurrentSlotHash() common.Hash
- GetStorageProcessed(common.Hash) bool
+ SetStorageProcessed(bool)
- GetCurrentPreimageOffset(common.Hash) int64
+ GetStorageProcessed() bool
- SetCurrentPreimageOffset(int64, common.Hash)
+ GetCurrentPreimageOffset() int64
+
+ SetCurrentPreimageOffset(int64)
AddRootTranslation(originalRoot, translatedRoot common.Hash)
SetLastMerkleRoot(common.Hash)
+
+ SaveTransitionState(common.Hash)
+
+ LoadTransitionState(common.Hash)
}
// Trie is a Ethereum Merkle Patricia trie.
@@ -182,40 +189,31 @@ func NewDatabase(db ethdb.Database) Database {
// large memory cache.
func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
return &cachingDB{
- disk: db,
- codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
- codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
- triedb: trie.NewDatabaseWithConfig(db, config),
- addrToPoint: utils.NewPointCache(),
- StorageProcessed: map[common.Hash]bool{},
- CurrentAccountAddress: map[common.Hash]*common.Address{},
- CurrentSlotHash: map[common.Hash]common.Hash{},
- CurrentPreimageOffset: map[common.Hash]int64{},
+ disk: db,
+ codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
+ codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
+ triedb: trie.NewDatabaseWithConfig(db, config),
+ addrToPoint: utils.NewPointCache(),
}
}
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
return &cachingDB{
- disk: db,
- codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
- codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
- triedb: triedb,
- addrToPoint: utils.NewPointCache(),
- ended: triedb.IsVerkle(),
- StorageProcessed: map[common.Hash]bool{},
- CurrentAccountAddress: map[common.Hash]*common.Address{},
- CurrentSlotHash: map[common.Hash]common.Hash{},
- CurrentPreimageOffset: map[common.Hash]int64{},
+ disk: db,
+ codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
+ codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
+ triedb: triedb,
+ addrToPoint: utils.NewPointCache(),
}
}
func (db *cachingDB) InTransition() bool {
- return db.started && !db.ended
+ return db.CurrentTransitionState != nil && db.CurrentTransitionState.started && !db.CurrentTransitionState.ended
}
func (db *cachingDB) Transitioned() bool {
- return db.ended
+ return db.CurrentTransitionState != nil && db.CurrentTransitionState.ended
}
// Fork implements the fork
@@ -227,29 +225,35 @@ func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.H
| | | Y \ ___/ \ ___/| |_\ ___/| |_> | Y \/ __ \| | | | | Y \/ __ \_\___ \ \ /| | | \\___ /\___ \
|____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ \/\_/ |__|___| /_____//_____/
|__|`)
- db.started = true
- db.ended = false
+ db.CurrentTransitionState = &TransitionState{
+ started: true,
+ // initialize so that the first storage-less accounts are processed
+ StorageProcessed: true,
+ }
// db.AddTranslation(originalRoot, translatedRoot)
db.baseRoot = originalRoot
- // initialize so that the first storage-less accounts are processed
- db.StorageProcessed[root] = true
// Reinitialize values in case of a reorg
- db.CurrentAccountAddress[root] = &(common.Address{})
- db.CurrentSlotHash[root] = common.Hash{}
- db.CurrentPreimageOffset[root] = 0
if pragueTime != nil {
chainConfig.PragueTime = pragueTime
}
}
func (db *cachingDB) ReorgThroughVerkleTransition() {
- db.ended, db.started = false, false
+ log.Warn("trying to reorg through the transition, which makes no sense at this point")
+}
+
+func (db *cachingDB) InitTransitionStatus(started, ended bool) {
+ db.CurrentTransitionState = &TransitionState{
+ ended: ended,
+ started: started,
+ // TODO add other fields when we handle mid-transition interrupts
+ }
}
func (db *cachingDB) EndVerkleTransition() {
- if !db.started {
- db.started = true
+ if !db.CurrentTransitionState.started {
+ db.CurrentTransitionState.started = true
}
fmt.Println(`
@@ -259,7 +263,35 @@ func (db *cachingDB) EndVerkleTransition() {
| | | Y \ ___/ \ ___/| |_\ ___/| |_> | Y \/ __ \| | | | | Y \/ __ \_\___ \ | |__/ __ \| | / /_/ \ ___// /_/ |
|____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ |____(____ |___| \____ |\___ \____ |
|__|`)
- db.ended = true
+ db.CurrentTransitionState.ended = true
+}
+
+type TransitionState struct {
+ CurrentAccountAddress *common.Address // addresss of the last translated account
+ CurrentSlotHash common.Hash // hash of the last translated storage slot
+ CurrentPreimageOffset int64 // next byte to read from the preimage file
+ started, ended bool
+
+ // Mark whether the storage for an account has been processed. This is useful if the
+ // maximum number of leaves of the conversion is reached before the whole storage is
+ // processed.
+ StorageProcessed bool
+}
+
+func (ts *TransitionState) Copy() *TransitionState {
+ ret := &TransitionState{
+ started: ts.started,
+ ended: ts.ended,
+ CurrentSlotHash: ts.CurrentSlotHash,
+ CurrentPreimageOffset: ts.CurrentPreimageOffset,
+ }
+
+ if ts.CurrentAccountAddress != nil {
+ ret.CurrentAccountAddress = &common.Address{}
+ copy(ret.CurrentAccountAddress[:], ts.CurrentAccountAddress[:])
+ }
+
+ return ret
}
type cachingDB struct {
@@ -268,22 +300,16 @@ type cachingDB struct {
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
triedb *trie.Database
- // Verkle specific fields
+ // Transition-specific fields
// TODO ensure that this info is in the DB
- started, ended bool
- LastMerkleRoot common.Hash // root hash of the read-only base tree
+ LastMerkleRoot common.Hash // root hash of the read-only base tree
+ CurrentTransitionState *TransitionState
+ TransitionStatePerRoot map[common.Hash]*TransitionState
addrToPoint *utils.PointCache
- baseRoot common.Hash // hash of the read-only base tree
- CurrentAccountAddress map[common.Hash]*common.Address // addresss of the last translated account
- CurrentSlotHash map[common.Hash]common.Hash // hash of the last translated storage slot
- CurrentPreimageOffset map[common.Hash]int64 // next byte to read from the preimage file
+ baseRoot common.Hash // hash of the read-only base tree
- // Mark whether the storage for an account has been processed. This is useful if the
- // maximum number of leaves of the conversion is reached before the whole storage is
- // processed.
- StorageProcessed map[common.Hash]bool
}
func (db *cachingDB) openMPTTrie(root common.Hash) (Trie, error) {
@@ -297,14 +323,14 @@ func (db *cachingDB) openMPTTrie(root common.Hash) (Trie, error) {
func (db *cachingDB) openVKTrie(root common.Hash) (Trie, error) {
payload, err := db.DiskDB().Get(trie.FlatDBVerkleNodeKeyPrefix)
if err != nil {
- return trie.NewVerkleTrie(verkle.New(), db.triedb, db.addrToPoint, db.ended), nil
+ return trie.NewVerkleTrie(verkle.New(), db.triedb, db.addrToPoint, db.CurrentTransitionState.ended), nil
}
r, err := verkle.ParseNode(payload, 0)
if err != nil {
panic(err)
}
- return trie.NewVerkleTrie(r, db.triedb, db.addrToPoint, db.ended), err
+ return trie.NewVerkleTrie(r, db.triedb, db.addrToPoint, db.CurrentTransitionState.ended), err
}
// OpenTrie opens the main account trie at a specific root hash.
@@ -316,7 +342,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// TODO separate both cases when I can be certain that it won't
// find a Verkle trie where is expects a Transitoion trie.
- if db.started || db.ended {
+ if db.CurrentTransitionState != nil && (db.CurrentTransitionState.started || db.CurrentTransitionState.ended) {
// NOTE this is a kaustinen-only change, it will break replay
vkt, err := db.openVKTrie(root)
if err != nil {
@@ -325,7 +351,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// If the verkle conversion has ended, return a single
// verkle trie.
- if db.ended {
+ if db.CurrentTransitionState.ended {
return vkt, nil
}
@@ -358,7 +384,7 @@ func (db *cachingDB) openStorageMPTrie(stateRoot common.Hash, address common.Add
// OpenStorageTrie opens the storage trie of an account
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
// TODO this should only return a verkle tree
- if db.ended {
+ if db.Transitioned() {
mpt, err := db.openStorageMPTrie(types.EmptyRootHash, address, common.Hash{}, self)
if err != nil {
return nil, err
@@ -374,7 +400,7 @@ func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
panic("unexpected trie type")
}
}
- if db.started {
+ if db.InTransition() {
mpt, err := db.openStorageMPTrie(db.LastMerkleRoot, address, root, nil)
if err != nil {
return nil, err
@@ -463,44 +489,44 @@ func (db *cachingDB) GetTreeKeyHeader(addr []byte) *verkle.Point {
return db.addrToPoint.GetTreeKeyHeader(addr)
}
-func (db *cachingDB) SetCurrentAccountAddress(addr common.Address, root common.Hash) {
- db.CurrentAccountAddress[root] = &addr
+func (db *cachingDB) SetCurrentAccountAddress(addr common.Address) {
+ db.CurrentTransitionState.CurrentAccountAddress = &addr
}
-func (db *cachingDB) GetCurrentAccountHash(root common.Hash) common.Hash {
+func (db *cachingDB) GetCurrentAccountHash() common.Hash {
var addrHash common.Hash
- if db.CurrentAccountAddress[root] != nil {
- addrHash = crypto.Keccak256Hash(db.CurrentAccountAddress[root][:])
+ if db.CurrentTransitionState.CurrentAccountAddress != nil {
+ addrHash = crypto.Keccak256Hash(db.CurrentTransitionState.CurrentAccountAddress[:])
}
return addrHash
}
-func (db *cachingDB) GetCurrentAccountAddress(root common.Hash) *common.Address {
- return db.CurrentAccountAddress[root]
+func (db *cachingDB) GetCurrentAccountAddress() *common.Address {
+ return db.CurrentTransitionState.CurrentAccountAddress
}
-func (db *cachingDB) GetCurrentPreimageOffset(root common.Hash) int64 {
- return db.CurrentPreimageOffset[root]
+func (db *cachingDB) GetCurrentPreimageOffset() int64 {
+ return db.CurrentTransitionState.CurrentPreimageOffset
}
-func (db *cachingDB) SetCurrentPreimageOffset(offset int64, root common.Hash) {
- db.CurrentPreimageOffset[root] = offset
+func (db *cachingDB) SetCurrentPreimageOffset(offset int64) {
+ db.CurrentTransitionState.CurrentPreimageOffset = offset
}
-func (db *cachingDB) SetCurrentSlotHash(hash common.Hash, root common.Hash) {
- db.CurrentSlotHash[root] = hash
+func (db *cachingDB) SetCurrentSlotHash(hash common.Hash) {
+ db.CurrentTransitionState.CurrentSlotHash = hash
}
-func (db *cachingDB) GetCurrentSlotHash(root common.Hash) common.Hash {
- return db.CurrentSlotHash[root]
+func (db *cachingDB) GetCurrentSlotHash() common.Hash {
+ return db.CurrentTransitionState.CurrentSlotHash
}
-func (db *cachingDB) SetStorageProcessed(processed bool, root common.Hash) {
- db.StorageProcessed[root] = processed
+func (db *cachingDB) SetStorageProcessed(processed bool) {
+ db.CurrentTransitionState.StorageProcessed = processed
}
-func (db *cachingDB) GetStorageProcessed(root common.Hash) bool {
- return db.StorageProcessed[root]
+func (db *cachingDB) GetStorageProcessed() bool {
+ return db.CurrentTransitionState.StorageProcessed
}
func (db *cachingDB) AddRootTranslation(originalRoot, translatedRoot common.Hash) {
@@ -509,3 +535,29 @@ func (db *cachingDB) AddRootTranslation(originalRoot, translatedRoot common.Hash
func (db *cachingDB) SetLastMerkleRoot(merkleRoot common.Hash) {
db.LastMerkleRoot = merkleRoot
}
+
+func (db *cachingDB) SaveTransitionState(root common.Hash) {
+ if db.TransitionStatePerRoot == nil {
+ db.TransitionStatePerRoot = make(map[common.Hash]*TransitionState)
+ }
+
+ db.TransitionStatePerRoot[root] = db.CurrentTransitionState
+}
+
+func (db *cachingDB) LoadTransitionState(root common.Hash) {
+ if db.TransitionStatePerRoot == nil {
+ db.TransitionStatePerRoot = make(map[common.Hash]*TransitionState)
+ }
+
+ ts, ok := db.TransitionStatePerRoot[root]
+ if !ok || ts == nil {
+ // Start with a fresh state
+ ts = &TransitionState{ended: db.triedb.IsVerkle()}
+ }
+
+ db.CurrentTransitionState = ts.Copy()
+
+ if db.CurrentTransitionState != nil {
+ fmt.Println("address", db.CurrentTransitionState.CurrentAccountAddress)
+ }
+}
diff --git a/core/state_processor.go b/core/state_processor.go
index 8cc8ac50ca3d..cb4d622d6d90 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -21,9 +21,6 @@ import (
"errors"
"fmt"
"math/big"
- "runtime"
- "sync"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
@@ -34,11 +31,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/utils"
- tutils "github.com/ethereum/go-ethereum/trie/utils"
- "github.com/ethereum/go-verkle"
- "github.com/holiman/uint256"
)
// StateProcessor is a basic Processor, which takes care of transitioning
@@ -115,10 +107,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
}
// Perform the overlay transition, if relevant
- parent := p.bc.GetHeaderByHash(header.ParentHash)
- if err := OverlayVerkleTransition(statedb, parent.Root); err != nil {
- return nil, nil, 0, fmt.Errorf("error performing verkle overlay transition: %w", err)
- }
+ //parent := p.bc.GetHeaderByHash(header.ParentHash)
+ //if err := OverlayVerkleTransition(statedb, parent.Root); err != nil {
+ // return nil, nil, 0, fmt.Errorf("error performing verkle overlay transition: %w", err)
+ //}
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), withdrawals)
@@ -194,190 +186,6 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
}
-var zeroTreeIndex uint256.Int
-
-// keyValueMigrator is a helper module that collects key-values from the overlay-tree migration for Verkle Trees.
-// It assumes that the walk of the base tree is done in address-order, so it exploit that fact to
-// collect the key-values in a way that is efficient.
-type keyValueMigrator struct {
- // leafData contains the values for the future leaf for a particular VKT branch.
- leafData []migratedKeyValue
-
- // When prepare() is called, it will start a background routine that will process the leafData
- // saving the result in newLeaves to be used by migrateCollectedKeyValues(). The background
- // routine signals that it is done by closing processingReady.
- processingReady chan struct{}
- newLeaves []verkle.LeafNode
- prepareErr error
-}
-
-func newKeyValueMigrator() *keyValueMigrator {
- // We do initialize the VKT config since prepare() might indirectly make multiple GetConfig() calls
- // in different goroutines when we never called GetConfig() before, causing a race considering the way
- // that `config` is designed in go-verkle.
- // TODO: jsign as a fix for this in the PR where we move to a file-less precomp, since it allows safe
- // concurrent calls to GetConfig(). When that gets merged, we can remove this line.
- _ = verkle.GetConfig()
- return &keyValueMigrator{
- processingReady: make(chan struct{}),
- leafData: make([]migratedKeyValue, 0, 10_000),
- }
-}
-
-type migratedKeyValue struct {
- branchKey branchKey
- leafNodeData verkle.BatchNewLeafNodeData
-}
-type branchKey struct {
- addr common.Address
- treeIndex uint256.Int
-}
-
-func newBranchKey(addr []byte, treeIndex *uint256.Int) branchKey {
- var sk branchKey
- copy(sk.addr[:], addr)
- sk.treeIndex = *treeIndex
- return sk
-}
-
-func (kvm *keyValueMigrator) addStorageSlot(addr []byte, slotNumber []byte, slotValue []byte) {
- treeIndex, subIndex := tutils.GetTreeKeyStorageSlotTreeIndexes(slotNumber)
- leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, treeIndex))
- leafNodeData.Values[subIndex] = slotValue
-}
-
-func (kvm *keyValueMigrator) addAccount(addr []byte, acc *types.StateAccount) {
- leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, &zeroTreeIndex))
-
- var version [verkle.LeafValueSize]byte
- leafNodeData.Values[tutils.VersionLeafKey] = version[:]
-
- var balance [verkle.LeafValueSize]byte
- for i, b := range acc.Balance.Bytes() {
- balance[len(acc.Balance.Bytes())-1-i] = b
- }
- leafNodeData.Values[tutils.BalanceLeafKey] = balance[:]
-
- var nonce [verkle.LeafValueSize]byte
- binary.LittleEndian.PutUint64(nonce[:8], acc.Nonce)
- leafNodeData.Values[tutils.NonceLeafKey] = nonce[:]
-
- leafNodeData.Values[tutils.CodeKeccakLeafKey] = acc.CodeHash[:]
-}
-
-func (kvm *keyValueMigrator) addAccountCode(addr []byte, codeSize uint64, chunks []byte) {
- leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, &zeroTreeIndex))
-
- // Save the code size.
- var codeSizeBytes [verkle.LeafValueSize]byte
- binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize)
- leafNodeData.Values[tutils.CodeSizeLeafKey] = codeSizeBytes[:]
-
- // The first 128 chunks are stored in the account header leaf.
- for i := 0; i < 128 && i < len(chunks)/32; i++ {
- leafNodeData.Values[byte(128+i)] = chunks[32*i : 32*(i+1)]
- }
-
- // Potential further chunks, have their own leaf nodes.
- for i := 128; i < len(chunks)/32; {
- treeIndex, _ := tutils.GetTreeKeyCodeChunkIndices(uint256.NewInt(uint64(i)))
- leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, treeIndex))
-
- j := i
- for ; (j-i) < 256 && j < len(chunks)/32; j++ {
- leafNodeData.Values[byte((j-128)%256)] = chunks[32*j : 32*(j+1)]
- }
- i = j
- }
-}
-
-func (kvm *keyValueMigrator) getOrInitLeafNodeData(bk branchKey) *verkle.BatchNewLeafNodeData {
- // Remember that keyValueMigration receives actions ordered by (address, subtreeIndex).
- // This means that we can assume that the last element of leafData is the one that we
- // are looking for, or that we need to create a new one.
- if len(kvm.leafData) == 0 || kvm.leafData[len(kvm.leafData)-1].branchKey != bk {
- kvm.leafData = append(kvm.leafData, migratedKeyValue{
- branchKey: bk,
- leafNodeData: verkle.BatchNewLeafNodeData{
- Stem: nil, // It will be calculated in the prepare() phase, since it's CPU heavy.
- Values: make(map[byte][]byte),
- },
- })
- }
- return &kvm.leafData[len(kvm.leafData)-1].leafNodeData
-}
-
-func (kvm *keyValueMigrator) prepare() {
- // We fire a background routine to process the leafData and save the result in newLeaves.
- // The background routine signals that it is done by closing processingReady.
- go func() {
- // Step 1: We split kvm.leafData in numBatches batches, and we process each batch in a separate goroutine.
- // This fills each leafNodeData.Stem with the correct value.
- var wg sync.WaitGroup
- batchNum := runtime.NumCPU()
- batchSize := (len(kvm.leafData) + batchNum - 1) / batchNum
- for i := 0; i < len(kvm.leafData); i += batchSize {
- start := i
- end := i + batchSize
- if end > len(kvm.leafData) {
- end = len(kvm.leafData)
- }
- wg.Add(1)
-
- batch := kvm.leafData[start:end]
- go func() {
- defer wg.Done()
- var currAddr common.Address
- var currPoint *verkle.Point
- for i := range batch {
- if batch[i].branchKey.addr != currAddr || currAddr == (common.Address{}) {
- currAddr = batch[i].branchKey.addr
- currPoint = tutils.EvaluateAddressPoint(currAddr[:])
- }
- stem := tutils.GetTreeKeyWithEvaluatedAddess(currPoint, &batch[i].branchKey.treeIndex, 0)
- stem = stem[:verkle.StemSize]
- batch[i].leafNodeData.Stem = stem
- }
- }()
- }
- wg.Wait()
-
- // Step 2: Now that we have all stems (i.e: tree keys) calculated, we can create the new leaves.
- nodeValues := make([]verkle.BatchNewLeafNodeData, len(kvm.leafData))
- for i := range kvm.leafData {
- nodeValues[i] = kvm.leafData[i].leafNodeData
- }
-
- // Create all leaves in batch mode so we can optimize cryptography operations.
- kvm.newLeaves, kvm.prepareErr = verkle.BatchNewLeafNode(nodeValues)
- close(kvm.processingReady)
- }()
-}
-
-func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) error {
- now := time.Now()
- <-kvm.processingReady
- if kvm.prepareErr != nil {
- return fmt.Errorf("failed to prepare key values: %w", kvm.prepareErr)
- }
- log.Info("Prepared key values from base tree", "duration", time.Since(now))
-
- // Insert into the tree.
- if err := tree.InsertMigratedLeaves(kvm.newLeaves); err != nil {
- return fmt.Errorf("failed to insert migrated leaves: %w", err)
- }
-
- return nil
-}
-
-func InsertBlockHashHistoryAtEip2935Fork(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash, chain consensus.ChainHeaderReader) {
- ancestor := chain.GetHeader(prevHash, prevNumber)
- for i := prevNumber; i > 0 && i >= prevNumber-256; i-- {
- ProcessParentBlockHash(statedb, i, ancestor.Hash())
- ancestor = chain.GetHeader(ancestor.ParentHash, ancestor.Number.Uint64()-1)
- }
-}
-
func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash) {
ringIndex := prevNumber % 256
var key common.Hash
diff --git a/light/trie.go b/light/trie.go
index 6d0c654ff111..df300c8c6ed2 100644
--- a/light/trie.go
+++ b/light/trie.go
@@ -121,39 +121,43 @@ func (db *odrDatabase) Transitioned() bool {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetCurrentSlotHash(common.Hash, common.Hash) {
+func (db *odrDatabase) InitTransitionStatus(bool, bool) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetCurrentAccountAddress(common.Hash) *common.Address {
+func (db *odrDatabase) SetCurrentSlotHash(common.Hash) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetCurrentAccountAddress(common.Address, common.Hash) {
+func (db *odrDatabase) GetCurrentAccountAddress() *common.Address {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetCurrentAccountHash(common.Hash) common.Hash {
+func (db *odrDatabase) SetCurrentAccountAddress(common.Address) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetCurrentSlotHash(common.Hash) common.Hash {
+func (db *odrDatabase) GetCurrentAccountHash() common.Hash {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetStorageProcessed(bool, common.Hash) {
+func (db *odrDatabase) GetCurrentSlotHash() common.Hash {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetStorageProcessed(common.Hash) bool {
+func (db *odrDatabase) SetStorageProcessed(bool) {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) GetCurrentPreimageOffset(common.Hash) int64 {
+func (db *odrDatabase) GetStorageProcessed() bool {
panic("not implemented") // TODO: Implement
}
-func (db *odrDatabase) SetCurrentPreimageOffset(int64, common.Hash) {
+func (db *odrDatabase) GetCurrentPreimageOffset() int64 {
+ panic("not implemented") // TODO: Implement
+}
+
+func (db *odrDatabase) SetCurrentPreimageOffset(int64) {
panic("not implemented") // TODO: Implement
}
@@ -165,6 +169,14 @@ func (db *odrDatabase) SetLastMerkleRoot(common.Hash) {
panic("not implemented") // TODO: Implement
}
+func (db *odrDatabase) SaveTransitionState(common.Hash) {
+ panic("not implemented") // TODO: Implement
+}
+
+func (db *odrDatabase) LoadTransitionState(common.Hash) {
+ panic("not implemented") // TODO: Implement
+}
+
type odrTrie struct {
db *odrDatabase
id *TrieID
diff --git a/miner/worker.go b/miner/worker.go
index 1d1b2fda07a2..9ac258c77591 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -904,9 +904,6 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
if err != nil {
return nil, err
}
- if w.chain.Config().IsPrague(header.Number, header.Time) {
- core.OverlayVerkleTransition(state, parent.Root)
- }
// Run the consensus preparation with the default or customized consensus engine.
if err := w.engine.Prepare(w.chain, header); err != nil {
log.Error("Failed to prepare header for sealing", "err", err)
From 0582870e68f67e5e36c475f2d7420fca4409a99a Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 5 Dec 2023 16:53:46 +0100
Subject: [PATCH 05/22] fix OpenStorageTrie: return an error instead of
panicking if the account tree not a verkle tree (#321)
---
core/state/database.go | 2 +-
core/state/trie_prefetcher.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/state/database.go b/core/state/database.go
index fcc41ba95927..175759c76d01 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -413,7 +413,7 @@ func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
case *trie.TransitionTrie:
return trie.NewTransitionTree(mpt.(*trie.SecureTrie), self.Overlay(), true), nil
default:
- panic("unexpected trie type")
+ return nil, errors.New("expected a verkle account tree, and found another type")
}
}
mpt, err := db.openStorageMPTrie(stateRoot, address, root, nil)
diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go
index 6c5c158cc239..4521736c7beb 100644
--- a/core/state/trie_prefetcher.go
+++ b/core/state/trie_prefetcher.go
@@ -302,7 +302,7 @@ func (sf *subfetcher) loop() {
}
sf.trie = trie
} else {
- trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil /* safe to set to nil for now, as there is no prefetcher for verkle */)
+ trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, sf.trie)
if err != nil {
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
return
From 3e5b4b74527948bbcbee7cfe2341b8491c827f18 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 5 Dec 2023 17:19:11 +0100
Subject: [PATCH 06/22] add a switch to force proof in blocks (#322)
* add a switch to force proof in blocks
* activate switch
* fix switch type
---
cmd/geth/config.go | 4 ++++
cmd/geth/main.go | 1 +
cmd/utils/flags.go | 5 +++++
core/blockchain.go | 7 +++++--
core/genesis.go | 5 +++--
eth/backend.go | 3 +++
eth/ethconfig/config.go | 3 +++
7 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index bf01c6f91857..4e861d75350b 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -175,6 +175,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
v := ctx.Uint64(utils.OverridePrague.Name)
cfg.Eth.OverridePrague = &v
}
+ if ctx.IsSet(utils.OverrideProofInBlock.Name) {
+ v := ctx.Bool(utils.OverrideProofInBlock.Name)
+ cfg.Eth.OverrideProofInBlock = &v
+ }
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
// Configure log filter RPC API.
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 38fb755b4b5a..2945a3c4b1f9 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -69,6 +69,7 @@ var (
utils.SmartCardDaemonPathFlag,
utils.OverrideCancun,
utils.OverridePrague,
+ utils.OverrideProofInBlock,
utils.EnablePersonal,
utils.TxPoolLocalsFlag,
utils.TxPoolNoLocalsFlag,
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index b927d0f94f83..5f071d0a7479 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -273,6 +273,11 @@ var (
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
Category: flags.EthCategory,
}
+ OverrideProofInBlock = &cli.BoolFlag{
+ Name: "override.blockproof",
+ Usage: "Manually specify the proof-in-block setting",
+ Category: flags.EthCategory,
+ }
// Light server and client settings
LightServeFlag = &cli.IntFlag{
Name: "light.serve",
diff --git a/core/blockchain.go b/core/blockchain.go
index 6dcd0e81fab3..0cc4115a486a 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -251,6 +251,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
}
+ if overrides.OverrideProofInBlock != nil {
+ chainConfig.ProofInBlocks = *overrides.OverrideProofInBlock
+ }
log.Info("")
log.Info(strings.Repeat("-", 153))
for _, line := range strings.Split(chainConfig.Description(), "\n") {
@@ -316,8 +319,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
// TODO this only works when resuming a chain that has already gone
// through the conversion. All pointers should be saved to the DB
// for it to be able to recover if interrupted during the transition
- // but that's left out to a later PR since there's not really a need
- // right now.
+ // but that's left out to a later PR since there's not really a need
+ // right now.
bc.stateCache.InitTransitionStatus(true, true)
bc.stateCache.EndVerkleTransition()
}
diff --git a/core/genesis.go b/core/genesis.go
index 0aad87a10d70..9fd19f03671d 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -291,8 +291,9 @@ func (e *GenesisMismatchError) Error() string {
// ChainOverrides contains the changes to chain config.
type ChainOverrides struct {
- OverrideCancun *uint64
- OverridePrague *uint64
+ OverrideCancun *uint64
+ OverridePrague *uint64
+ OverrideProofInBlock *bool
}
// SetupGenesisBlock writes or updates the genesis block in db.
diff --git a/eth/backend.go b/eth/backend.go
index a6c80159077d..c5be460c0d91 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -201,6 +201,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if config.OverridePrague != nil {
overrides.OverridePrague = config.OverridePrague
}
+ if config.OverrideProofInBlock != nil {
+ overrides.OverrideProofInBlock = config.OverrideProofInBlock
+ }
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
if err != nil {
return nil, err
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index 4606b60408dd..b9660f5c60b3 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -158,6 +158,9 @@ type Config struct {
// OverrideVerkle (TODO: remove after the fork)
OverridePrague *uint64 `toml:",omitempty"`
+
+ // OverrideProofInBlock
+ OverrideProofInBlock *bool `toml:",omitempty"`
}
// CreateConsensusEngine creates a consensus engine for the given chain config.
From 8bfaaa01e76589fd53c77e45beda10b977840f69 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 5 Dec 2023 17:44:28 +0100
Subject: [PATCH 07/22] add switch to override the stride of the overlay
conversion (#323)
* add switch to override the stride of the overlay conversion
* set a default stride of 10k
---
cmd/geth/config.go | 4 ++++
cmd/geth/main.go | 1 +
cmd/utils/flags.go | 6 ++++++
consensus/beacon/consensus.go | 2 +-
core/blockchain.go | 3 +++
core/genesis.go | 7 ++++---
core/overlay/conversion.go | 5 ++---
eth/ethconfig/config.go | 3 +++
params/config.go | 3 ++-
9 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index 4e861d75350b..cdd893d2a8ec 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -179,6 +179,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
v := ctx.Bool(utils.OverrideProofInBlock.Name)
cfg.Eth.OverrideProofInBlock = &v
}
+ if ctx.IsSet(utils.OverrideOverlayStride.Name) {
+ v := ctx.Uint64(utils.OverrideOverlayStride.Name)
+ cfg.Eth.OverrideOverlayStride = &v
+ }
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
// Configure log filter RPC API.
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 2945a3c4b1f9..e2e4ba25fe06 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -67,6 +67,7 @@ var (
utils.NoUSBFlag,
utils.USBFlag,
utils.SmartCardDaemonPathFlag,
+ utils.OverrideOverlayStride,
utils.OverrideCancun,
utils.OverridePrague,
utils.OverrideProofInBlock,
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 5f071d0a7479..0dd311706abb 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -263,6 +263,12 @@ var (
Value: 2048,
Category: flags.EthCategory,
}
+ OverrideOverlayStride = &cli.Uint64Flag{
+ Name: "override.overlay-stride",
+ Usage: "Manually specify the stride of the overlay transition, overriding the bundled setting",
+ Value: 10000,
+ Category: flags.EthCategory,
+ }
OverrideCancun = &cli.Uint64Flag{
Name: "override.cancun",
Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting",
diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go
index dd94b8da7790..70008b270259 100644
--- a/consensus/beacon/consensus.go
+++ b/consensus/beacon/consensus.go
@@ -368,7 +368,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
if chain.Config().IsPrague(header.Number, header.Time) {
fmt.Println("at block", header.Number, "performing transition?", state.Database().InTransition())
parent := chain.GetHeaderByHash(header.ParentHash)
- overlay.OverlayVerkleTransition(state, parent.Root)
+ overlay.OverlayVerkleTransition(state, parent.Root, chain.Config().OverlayStride)
}
}
diff --git a/core/blockchain.go b/core/blockchain.go
index 0cc4115a486a..1008ca692896 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -254,6 +254,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
if overrides.OverrideProofInBlock != nil {
chainConfig.ProofInBlocks = *overrides.OverrideProofInBlock
}
+ if overrides.OverrideOverlayStride != nil {
+ chainConfig.OverlayStride = *overrides.OverrideOverlayStride
+ }
log.Info("")
log.Info(strings.Repeat("-", 153))
for _, line := range strings.Split(chainConfig.Description(), "\n") {
diff --git a/core/genesis.go b/core/genesis.go
index 9fd19f03671d..a2a331d1fe33 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -291,9 +291,10 @@ func (e *GenesisMismatchError) Error() string {
// ChainOverrides contains the changes to chain config.
type ChainOverrides struct {
- OverrideCancun *uint64
- OverridePrague *uint64
- OverrideProofInBlock *bool
+ OverrideCancun *uint64
+ OverridePrague *uint64
+ OverrideProofInBlock *bool
+ OverrideOverlayStride *uint64
}
// SetupGenesisBlock writes or updates the genesis block in db.
diff --git a/core/overlay/conversion.go b/core/overlay/conversion.go
index e76aa5900173..0e9047920a0b 100644
--- a/core/overlay/conversion.go
+++ b/core/overlay/conversion.go
@@ -217,7 +217,7 @@ func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) er
}
// OverlayVerkleTransition contains the overlay conversion logic
-func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
+func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedCount uint64) error {
migrdb := statedb.Database()
// verkle transition: if the conversion process is in progress, move
@@ -274,14 +274,13 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash) error {
preimageSeek += int64(len(addr))
}
- const maxMovedCount = 10000
// mkv will be assiting in the collection of up to maxMovedCount key values to be migrated to the VKT.
// It has internal caches to do efficient MPT->VKT key calculations, which will be discarded after
// this function.
mkv := newKeyValueMigrator()
// move maxCount accounts into the verkle tree, starting with the
// slots from the previous account.
- count := 0
+ count := uint64(0)
// if less than maxCount slots were moved, move to the next account
for count < maxMovedCount {
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index b9660f5c60b3..fc9550147bcc 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -161,6 +161,9 @@ type Config struct {
// OverrideProofInBlock
OverrideProofInBlock *bool `toml:",omitempty"`
+
+ // OverrideOverlayStride
+ OverrideOverlayStride *uint64 `toml:",omitempty"`
}
// CreateConsensusEngine creates a consensus engine for the given chain config.
diff --git a/params/config.go b/params/config.go
index 19e633a71def..a2df06893a22 100644
--- a/params/config.go
+++ b/params/config.go
@@ -301,7 +301,8 @@ type ChainConfig struct {
IsDevMode bool `json:"isDev,omitempty"`
// Proof in block
- ProofInBlocks bool `json:"proofInBlocks,omitempty"`
+ ProofInBlocks bool `json:"proofInBlocks,omitempty"`
+ OverlayStride uint64 `json:"overlayStride,omitempty"`
}
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
From 677e66ea65223c0fc7796d8ce4000b4d1b909261 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 5 Dec 2023 17:55:39 +0100
Subject: [PATCH 08/22] add a few traces for relaunch
---
core/overlay/conversion.go | 1 +
core/state/database.go | 1 +
2 files changed, 2 insertions(+)
diff --git a/core/overlay/conversion.go b/core/overlay/conversion.go
index 0e9047920a0b..1a987ab69250 100644
--- a/core/overlay/conversion.go
+++ b/core/overlay/conversion.go
@@ -223,6 +223,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
// verkle transition: if the conversion process is in progress, move
// N values from the MPT into the verkle tree.
if migrdb.InTransition() {
+ fmt.Printf("Processing verkle conversion starting at %x %x, building on top of %x\n", migrdb.GetCurrentAccountHash(), migrdb.GetCurrentSlotHash(), root)
var (
now = time.Now()
tt = statedb.GetTrie().(*trie.TransitionTrie)
diff --git a/core/state/database.go b/core/state/database.go
index 175759c76d01..5baaedb9d786 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -401,6 +401,7 @@ func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
}
}
if db.InTransition() {
+ fmt.Printf("OpenStorageTrie during transition, state root=%x root=%x\n", stateRoot, root)
mpt, err := db.openStorageMPTrie(db.LastMerkleRoot, address, root, nil)
if err != nil {
return nil, err
From 28eee603d117610eb07ef6368c2777739d6618d5 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Wed, 6 Dec 2023 11:42:53 +0100
Subject: [PATCH 09/22] add missing step in the chain of setting the override
for overlay stride
---
eth/backend.go | 3 +++
1 file changed, 3 insertions(+)
diff --git a/eth/backend.go b/eth/backend.go
index c5be460c0d91..c47bc6b5bb35 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -204,6 +204,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if config.OverrideProofInBlock != nil {
overrides.OverrideProofInBlock = config.OverrideProofInBlock
}
+ if config.OverrideOverlayStride != nil {
+ overrides.OverrideOverlayStride = config.OverrideOverlayStride
+ }
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
if err != nil {
return nil, err
From 3cbf6015ceaa9b1721feafa82e7a514ee3f19d54 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Fri, 8 Dec 2023 15:02:38 +0100
Subject: [PATCH 10/22] fix: save and load transition state for block
processing (#324)
* fix: save and load transition state for block processing
* log whether the tree is verkle in LoadTransitionState
* fix: ensure the transition is marked as started in insertChain
* dump saved address
* fix nil pointer panic
* remove stacktrace that is no longer useful
* fix a panic
* fix build
* check: copy current account address BEFORE it's saved
* mandatory panic fix
* Remove debug fmt.Println
* more cleanup + comments
---
core/block_validator.go | 1 +
core/blockchain.go | 9 +++++++++
core/state/database.go | 14 ++++++++------
3 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/core/block_validator.go b/core/block_validator.go
index b1ceab9d5c6c..d977c1d63d96 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -131,6 +131,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
}
+ statedb.Database().SaveTransitionState(header.Root)
return nil
}
diff --git a/core/blockchain.go b/core/blockchain.go
index 1008ca692896..fdde955b87bd 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -1758,6 +1758,15 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
}
+ if bc.Config().IsPrague(block.Number(), block.Time()) {
+ bc.stateCache.LoadTransitionState(parent.Root)
+
+ // pragueTime has been reached. If the transition isn't active, it means this
+ // is the fork block and that the conversion needs to be marked at started.
+ if !bc.stateCache.InTransition() && !bc.stateCache.Transitioned() {
+ bc.stateCache.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), bc.Config().PragueTime, parent.Root)
+ }
+ }
if parent.Number.Uint64() == conversionBlock {
bc.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), &parent.Time, parent.Root)
bc.stateCache.SetLastMerkleRoot(parent.Root)
diff --git a/core/state/database.go b/core/state/database.go
index 5baaedb9d786..c9d06b443b18 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -342,7 +342,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// TODO separate both cases when I can be certain that it won't
// find a Verkle trie where is expects a Transitoion trie.
- if db.CurrentTransitionState != nil && (db.CurrentTransitionState.started || db.CurrentTransitionState.ended) {
+ if db.InTransition() || db.Transitioned() {
// NOTE this is a kaustinen-only change, it will break replay
vkt, err := db.openVKTrie(root)
if err != nil {
@@ -542,7 +542,11 @@ func (db *cachingDB) SaveTransitionState(root common.Hash) {
db.TransitionStatePerRoot = make(map[common.Hash]*TransitionState)
}
- db.TransitionStatePerRoot[root] = db.CurrentTransitionState
+ if db.CurrentTransitionState != nil {
+ // Copy so that the address pointer isn't updated after
+ // it has been saved.
+ db.TransitionStatePerRoot[root] = db.CurrentTransitionState.Copy()
+ }
}
func (db *cachingDB) LoadTransitionState(root common.Hash) {
@@ -556,9 +560,7 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
ts = &TransitionState{ended: db.triedb.IsVerkle()}
}
+ // Copy so that the CurrentAddress pointer in the map
+ // doesn't get overwritten.
db.CurrentTransitionState = ts.Copy()
-
- if db.CurrentTransitionState != nil {
- fmt.Println("address", db.CurrentTransitionState.CurrentAccountAddress)
- }
}
From 7e86b8058a9230c27cca600ceb7716412c2c193c Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Sun, 10 Dec 2023 20:34:08 +0100
Subject: [PATCH 11/22] fix: ensure StorageProcessed is properly recovered
---
core/overlay/conversion.go | 2 +-
core/state/database.go | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/core/overlay/conversion.go b/core/overlay/conversion.go
index 1a987ab69250..929357203380 100644
--- a/core/overlay/conversion.go
+++ b/core/overlay/conversion.go
@@ -405,7 +405,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
}
migrdb.SetCurrentPreimageOffset(preimageSeek)
- log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account", statedb.Database().GetCurrentAccountHash())
+ log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account", statedb.Database().GetCurrentAccountHash(), "storage processed", statedb.Database().GetStorageProcessed(), "last storage", statedb.Database().GetCurrentSlotHash())
// Take all the collected key-values and prepare the new leaf values.
// This fires a background routine that will start doing the work that
diff --git a/core/state/database.go b/core/state/database.go
index c9d06b443b18..55d5dac739ff 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -284,6 +284,7 @@ func (ts *TransitionState) Copy() *TransitionState {
ended: ts.ended,
CurrentSlotHash: ts.CurrentSlotHash,
CurrentPreimageOffset: ts.CurrentPreimageOffset,
+ StorageProcessed: ts.StorageProcessed,
}
if ts.CurrentAccountAddress != nil {
@@ -563,4 +564,6 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
// Copy so that the CurrentAddress pointer in the map
// doesn't get overwritten.
db.CurrentTransitionState = ts.Copy()
+
+ fmt.Println("loaded transition state", db.CurrentTransitionState.StorageProcessed)
}
From 6a255d0de6ba55647701a414fb7d975c7eea2090 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 11 Dec 2023 16:26:20 +0100
Subject: [PATCH 12/22] add traces for gas pool overflow
---
core/state_transition.go | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/core/state_transition.go b/core/state_transition.go
index 969e7a75fb9b..5b89b89c4f29 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -508,6 +508,10 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice)
st.state.AddBalance(st.msg.From, remaining)
+ if st.gp.Gas() > math.MaxUint64-st.gasRemaining {
+ fmt.Println(st.gp.Gas(), refund, refundQuotient, st.gasUsed(), st.initialGas, st.gasUsed(), st.evm.Accesses, st.msg)
+ }
+
// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
st.gp.AddGas(st.gasRemaining)
From 9b54f2925a089355b0d562722504f29fed0f4fde Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Fri, 26 Jan 2024 17:57:55 +0100
Subject: [PATCH 13/22] cmd/{geth, utils}: add a command to clear verkle costs
(#326)
* cmd/{geth, utils}: add a command to clear verkle costs
fix: boolean issue
fix: load finalization state in FinalizeAndAssemble (#340)
* Conversion and TransitionTrie fixes (#346)
* fixes
Signed-off-by: Ignacio Hagopian
* remove old comment
Signed-off-by: Ignacio Hagopian
---------
Signed-off-by: Ignacio Hagopian
* trace cleanup
---------
Signed-off-by: Ignacio Hagopian
Co-authored-by: Ignacio Hagopian
---
cmd/geth/config.go | 3 ++
cmd/geth/main.go | 1 +
cmd/utils/flags.go | 5 ++
consensus/beacon/consensus.go | 88 +++++++++++++++++++----------------
core/blockchain.go | 7 +--
core/overlay/conversion.go | 47 ++++++++++++-------
core/state/database.go | 11 ++++-
core/state/statedb.go | 2 +-
miner/worker.go | 2 +-
trie/transition.go | 8 +++-
10 files changed, 109 insertions(+), 65 deletions(-)
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index cdd893d2a8ec..4184290e86c6 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -183,6 +183,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
v := ctx.Uint64(utils.OverrideOverlayStride.Name)
cfg.Eth.OverrideOverlayStride = &v
}
+ if ctx.IsSet(utils.ClearVerkleCosts.Name) {
+ params.ClearVerkleWitnessCosts()
+ }
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
// Configure log filter RPC API.
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index e2e4ba25fe06..5cb1580df3d1 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -71,6 +71,7 @@ var (
utils.OverrideCancun,
utils.OverridePrague,
utils.OverrideProofInBlock,
+ utils.ClearVerkleCosts,
utils.EnablePersonal,
utils.TxPoolLocalsFlag,
utils.TxPoolNoLocalsFlag,
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 0dd311706abb..3395eebca866 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -284,6 +284,11 @@ var (
Usage: "Manually specify the proof-in-block setting",
Category: flags.EthCategory,
}
+ ClearVerkleCosts = &cli.BoolFlag{
+ Name: "clear.verkle.costs",
+ Usage: "Clear verkle costs (for shadow forks)",
+ Category: flags.EthCategory,
+ }
// Light server and client settings
LightServeFlag = &cli.IntFlag{
Name: "light.serve",
diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go
index 70008b270259..7c93844f39c9 100644
--- a/consensus/beacon/consensus.go
+++ b/consensus/beacon/consensus.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/overlay"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
@@ -368,7 +369,9 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
if chain.Config().IsPrague(header.Number, header.Time) {
fmt.Println("at block", header.Number, "performing transition?", state.Database().InTransition())
parent := chain.GetHeaderByHash(header.ParentHash)
- overlay.OverlayVerkleTransition(state, parent.Root, chain.Config().OverlayStride)
+ if err := overlay.OverlayVerkleTransition(state, parent.Root, chain.Config().OverlayStride); err != nil {
+ log.Error("error performing the transition", "err", err)
+ }
}
}
@@ -394,63 +397,68 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
// Assign the final state root to header.
header.Root = state.IntermediateRoot(true)
+ state.Database().SaveTransitionState(header.Root)
var (
p *verkle.VerkleProof
k verkle.StateDiff
keys = state.Witness().Keys()
)
- if chain.Config().IsPrague(header.Number, header.Time) && chain.Config().ProofInBlocks {
+ if chain.Config().IsPrague(header.Number, header.Time) {
// Open the pre-tree to prove the pre-state against
parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1)
if parent == nil {
return nil, fmt.Errorf("nil parent header for block %d", header.Number)
}
- preTrie, err := state.Database().OpenTrie(parent.Root)
- if err != nil {
- return nil, fmt.Errorf("error opening pre-state tree root: %w", err)
- }
+ state.Database().LoadTransitionState(parent.Root)
+
+ if chain.Config().ProofInBlocks {
+ preTrie, err := state.Database().OpenTrie(parent.Root)
+ if err != nil {
+ return nil, fmt.Errorf("error opening pre-state tree root: %w", err)
+ }
- var okpre, okpost bool
- var vtrpre, vtrpost *trie.VerkleTrie
- switch pre := preTrie.(type) {
- case *trie.VerkleTrie:
- vtrpre, okpre = preTrie.(*trie.VerkleTrie)
- switch tr := state.GetTrie().(type) {
+ var okpre, okpost bool
+ var vtrpre, vtrpost *trie.VerkleTrie
+ switch pre := preTrie.(type) {
case *trie.VerkleTrie:
- vtrpost = tr
- okpost = true
- // This is to handle a situation right at the start of the conversion:
- // the post trie is a transition tree when the pre tree is an empty
- // verkle tree.
+ vtrpre, okpre = preTrie.(*trie.VerkleTrie)
+ switch tr := state.GetTrie().(type) {
+ case *trie.VerkleTrie:
+ vtrpost = tr
+ okpost = true
+ // This is to handle a situation right at the start of the conversion:
+ // the post trie is a transition tree when the pre tree is an empty
+ // verkle tree.
+ case *trie.TransitionTrie:
+ vtrpost = tr.Overlay()
+ okpost = true
+ default:
+ okpost = false
+ }
case *trie.TransitionTrie:
- vtrpost = tr.Overlay()
+ vtrpre = pre.Overlay()
+ okpre = true
+ post, _ := state.GetTrie().(*trie.TransitionTrie)
+ vtrpost = post.Overlay()
okpost = true
default:
- okpost = false
+ // This should only happen for the first block of the
+ // conversion, when the previous tree is a merkle tree.
+ // Logically, the "previous" verkle tree is an empty tree.
+ okpre = true
+ vtrpre = trie.NewVerkleTrie(verkle.New(), state.Database().TrieDB(), utils.NewPointCache(), false)
+ post := state.GetTrie().(*trie.TransitionTrie)
+ vtrpost = post.Overlay()
+ okpost = true
}
- case *trie.TransitionTrie:
- vtrpre = pre.Overlay()
- okpre = true
- post, _ := state.GetTrie().(*trie.TransitionTrie)
- vtrpost = post.Overlay()
- okpost = true
- default:
- // This should only happen for the first block of the
- // conversion, when the previous tree is a merkle tree.
- // Logically, the "previous" verkle tree is an empty tree.
- okpre = true
- vtrpre = trie.NewVerkleTrie(verkle.New(), state.Database().TrieDB(), utils.NewPointCache(), false)
- post := state.GetTrie().(*trie.TransitionTrie)
- vtrpost = post.Overlay()
- okpost = true
- }
- if okpre && okpost {
- if len(keys) > 0 {
- p, k, err = trie.ProveAndSerialize(vtrpre, vtrpost, keys, vtrpre.FlatdbNodeResolver)
- if err != nil {
- return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err)
+ if okpre && okpost {
+ if len(keys) > 0 {
+ p, k, err = trie.ProveAndSerialize(vtrpre, vtrpost, keys, vtrpre.FlatdbNodeResolver)
+ if err != nil {
+ return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err)
+ }
}
}
}
diff --git a/core/blockchain.go b/core/blockchain.go
index fdde955b87bd..c92e0e36f631 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -103,7 +103,7 @@ const (
txLookupCacheLimit = 1024
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
- TriesInMemory = 128
+ TriesInMemory = 8192
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
//
@@ -2010,7 +2010,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1)
}
if parent == nil {
- return it.index, errors.New("missing parent")
+ return it.index, fmt.Errorf("missing parent: hash=%x, number=%d", current.Hash(), current.Number)
}
// Import all the pruned blocks to make the state available
var (
@@ -2071,7 +2071,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
}
}
if parent == nil {
- return common.Hash{}, errors.New("missing parent")
+ return common.Hash{}, fmt.Errorf("missing parent during ancestor recovery: hash=%x, number=%d", block.ParentHash(), block.Number())
}
// Import all the pruned blocks to make the state available
for i := len(hashes) - 1; i >= 0; i-- {
@@ -2309,6 +2309,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
defer bc.chainmu.Unlock()
// Re-execute the reorged chain in case the head state is missing.
+ log.Trace("looking for state", "root", head.Root(), "has state", bc.HasState(head.Root()))
if !bc.HasState(head.Root()) {
if latestValidHash, err := bc.recoverAncestors(head); err != nil {
return latestValidHash, err
diff --git a/core/overlay/conversion.go b/core/overlay/conversion.go
index 929357203380..e538c58bcce0 100644
--- a/core/overlay/conversion.go
+++ b/core/overlay/conversion.go
@@ -47,7 +47,7 @@ var zeroTreeIndex uint256.Int
// collect the key-values in a way that is efficient.
type keyValueMigrator struct {
// leafData contains the values for the future leaf for a particular VKT branch.
- leafData []migratedKeyValue
+ leafData map[branchKey]*migratedKeyValue
// When prepare() is called, it will start a background routine that will process the leafData
// saving the result in newLeaves to be used by migrateCollectedKeyValues(). The background
@@ -66,7 +66,7 @@ func newKeyValueMigrator() *keyValueMigrator {
_ = verkle.GetConfig()
return &keyValueMigrator{
processingReady: make(chan struct{}),
- leafData: make([]migratedKeyValue, 0, 10_000),
+ leafData: make(map[branchKey]*migratedKeyValue, 10_000),
}
}
@@ -138,19 +138,17 @@ func (kvm *keyValueMigrator) addAccountCode(addr []byte, codeSize uint64, chunks
}
func (kvm *keyValueMigrator) getOrInitLeafNodeData(bk branchKey) *verkle.BatchNewLeafNodeData {
- // Remember that keyValueMigration receives actions ordered by (address, subtreeIndex).
- // This means that we can assume that the last element of leafData is the one that we
- // are looking for, or that we need to create a new one.
- if len(kvm.leafData) == 0 || kvm.leafData[len(kvm.leafData)-1].branchKey != bk {
- kvm.leafData = append(kvm.leafData, migratedKeyValue{
- branchKey: bk,
- leafNodeData: verkle.BatchNewLeafNodeData{
- Stem: nil, // It will be calculated in the prepare() phase, since it's CPU heavy.
- Values: make(map[byte][]byte),
- },
- })
+ if ld, ok := kvm.leafData[bk]; ok {
+ return &ld.leafNodeData
}
- return &kvm.leafData[len(kvm.leafData)-1].leafNodeData
+ kvm.leafData[bk] = &migratedKeyValue{
+ branchKey: bk,
+ leafNodeData: verkle.BatchNewLeafNodeData{
+ Stem: nil, // It will be calculated in the prepare() phase, since it's CPU heavy.
+ Values: make(map[byte][]byte, 256),
+ },
+ }
+ return &kvm.leafData[bk].leafNodeData
}
func (kvm *keyValueMigrator) prepare() {
@@ -159,6 +157,10 @@ func (kvm *keyValueMigrator) prepare() {
go func() {
// Step 1: We split kvm.leafData in numBatches batches, and we process each batch in a separate goroutine.
// This fills each leafNodeData.Stem with the correct value.
+ leafData := make([]migratedKeyValue, 0, len(kvm.leafData))
+ for _, v := range kvm.leafData {
+ leafData = append(leafData, *v)
+ }
var wg sync.WaitGroup
batchNum := runtime.NumCPU()
batchSize := (len(kvm.leafData) + batchNum - 1) / batchNum
@@ -170,7 +172,7 @@ func (kvm *keyValueMigrator) prepare() {
}
wg.Add(1)
- batch := kvm.leafData[start:end]
+ batch := leafData[start:end]
go func() {
defer wg.Done()
var currAddr common.Address
@@ -190,8 +192,8 @@ func (kvm *keyValueMigrator) prepare() {
// Step 2: Now that we have all stems (i.e: tree keys) calculated, we can create the new leaves.
nodeValues := make([]verkle.BatchNewLeafNodeData, len(kvm.leafData))
- for i := range kvm.leafData {
- nodeValues[i] = kvm.leafData[i].leafNodeData
+ for i := range leafData {
+ nodeValues[i] = leafData[i].leafNodeData
}
// Create all leaves in batch mode so we can optimize cryptography operations.
@@ -306,7 +308,12 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
if err != nil {
return err
}
- stIt.Next()
+ processed := stIt.Next()
+ if processed {
+ log.Debug("account has storage and a next item")
+ } else {
+ log.Debug("account has storage and NO next item")
+ }
// fdb.StorageProcessed will be initialized to `true` if the
// entire storage for an account was not entirely processed
@@ -315,6 +322,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
// If the entire storage was processed, then the iterator was
// created in vain, but it's ok as this will not happen often.
for ; !migrdb.GetStorageProcessed() && count < maxMovedCount; count++ {
+ log.Trace("Processing storage", "count", count, "slot", stIt.Slot(), "storage processed", migrdb.GetStorageProcessed(), "current account", migrdb.GetCurrentAccountAddress(), "current account hash", migrdb.GetCurrentAccountHash())
var (
value []byte // slot value after RLP decoding
safeValue [32]byte // 32-byte aligned value
@@ -337,6 +345,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
return fmt.Errorf("slotnr len is zero is not 32: %d", len(slotnr))
}
}
+ log.Trace("found slot number", "number", slotnr)
if crypto.Keccak256Hash(slotnr[:]) != stIt.Hash() {
return fmt.Errorf("preimage file does not match storage hash: %s!=%s", crypto.Keccak256Hash(slotnr), stIt.Hash())
}
@@ -378,6 +387,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
// Move to the next account, if available - or end
// the transition otherwise.
if accIt.Next() {
+ log.Trace("Found another account to convert", "hash", accIt.Hash())
var addr common.Address
if hasPreimagesBin {
if _, err := io.ReadFull(fpreimages, addr[:]); err != nil {
@@ -393,6 +403,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
if crypto.Keccak256Hash(addr[:]) != accIt.Hash() {
return fmt.Errorf("preimage file does not match account hash: %s != %s", crypto.Keccak256Hash(addr[:]), accIt.Hash())
}
+ log.Trace("Converting account address", "hash", accIt.Hash(), "addr", addr)
preimageSeek += int64(len(addr))
migrdb.SetCurrentAccountAddress(addr)
} else {
diff --git a/core/state/database.go b/core/state/database.go
index 55d5dac739ff..ab714837d411 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -340,6 +340,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
mpt Trie
err error
)
+ fmt.Printf("opening trie with root %x, %v %v\n", root, db.InTransition(), db.Transitioned())
// TODO separate both cases when I can be certain that it won't
// find a Verkle trie where is expects a Transitoion trie.
@@ -347,12 +348,14 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// NOTE this is a kaustinen-only change, it will break replay
vkt, err := db.openVKTrie(root)
if err != nil {
+ log.Error("failed to open the vkt", "err", err)
return nil, err
}
// If the verkle conversion has ended, return a single
// verkle trie.
if db.CurrentTransitionState.ended {
+ log.Debug("transition ended, returning a simple verkle tree")
return vkt, nil
}
@@ -360,6 +363,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// trie and an overlay, verkle trie.
mpt, err = db.openMPTTrie(db.baseRoot)
if err != nil {
+ log.Error("failed to open the mpt", "err", err, "root", db.baseRoot)
return nil, err
}
@@ -547,6 +551,8 @@ func (db *cachingDB) SaveTransitionState(root common.Hash) {
// Copy so that the address pointer isn't updated after
// it has been saved.
db.TransitionStatePerRoot[root] = db.CurrentTransitionState.Copy()
+
+ log.Debug("saving transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
}
}
@@ -555,6 +561,9 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
db.TransitionStatePerRoot = make(map[common.Hash]*TransitionState)
}
+ // Initialize the first transition state, with the "ended"
+ // field set to true if the database was created
+ // as a verkle database.
ts, ok := db.TransitionStatePerRoot[root]
if !ok || ts == nil {
// Start with a fresh state
@@ -565,5 +574,5 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
// doesn't get overwritten.
db.CurrentTransitionState = ts.Copy()
- fmt.Println("loaded transition state", db.CurrentTransitionState.StorageProcessed)
+ log.Debug("loaded transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index a10c5252f113..6ce3b9b2b9b2 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -1318,7 +1318,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
// - head layer is paired with HEAD state
// - head-1 layer is paired with HEAD-1 state
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
- if err := s.snaps.Cap(root, 128); err != nil {
+ if err := s.snaps.Cap(root, 8192); err != nil {
log.Warn("Failed to cap snapshot tree", "root", root, "layers", 128, "err", err)
}
}
diff --git a/miner/worker.go b/miner/worker.go
index 9ac258c77591..3fb4a3fa43e5 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -852,7 +852,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
if genParams.parentHash != (common.Hash{}) {
block := w.chain.GetBlockByHash(genParams.parentHash)
if block == nil {
- return nil, fmt.Errorf("missing parent")
+ return nil, fmt.Errorf("missing parent: %x", genParams.parentHash)
}
parent = block.Header()
}
diff --git a/trie/transition.go b/trie/transition.go
index 24daf436ed8a..0fe197336524 100644
--- a/trie/transition.go
+++ b/trie/transition.go
@@ -17,6 +17,8 @@
package trie
import (
+ "fmt"
+
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
@@ -62,7 +64,11 @@ func (t *TransitionTrie) GetKey(key []byte) []byte {
// not be modified by the caller. If a node was not found in the database, a
// trie.MissingNodeError is returned.
func (t *TransitionTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
- if val, err := t.overlay.GetStorage(addr, key); len(val) != 0 || err != nil {
+ val, err := t.overlay.GetStorage(addr, key)
+ if err != nil {
+ return nil, fmt.Errorf("get storage from overlay: %s", err)
+ }
+ if len(val) != 0 {
return val, nil
}
// TODO also insert value into overlay
From 68410635fb7f5b446887c052bd3a5835a187295b Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Sat, 3 Feb 2024 09:26:47 +0100
Subject: [PATCH 14/22] fix: check for nil override in NewBlockChain (#357)
---
core/blockchain.go | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/core/blockchain.go b/core/blockchain.go
index c92e0e36f631..36e58ca8df57 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -251,11 +251,13 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
}
- if overrides.OverrideProofInBlock != nil {
- chainConfig.ProofInBlocks = *overrides.OverrideProofInBlock
- }
- if overrides.OverrideOverlayStride != nil {
- chainConfig.OverlayStride = *overrides.OverrideOverlayStride
+ if overrides != nil {
+ if overrides.OverrideProofInBlock != nil {
+ chainConfig.ProofInBlocks = *overrides.OverrideProofInBlock
+ }
+ if overrides.OverrideOverlayStride != nil {
+ chainConfig.OverlayStride = *overrides.OverrideOverlayStride
+ }
}
log.Info("")
log.Info(strings.Repeat("-", 153))
From a4c556dbf0364c011a623d7f5236b9198c644b1c Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 8 Feb 2024 13:10:43 +0100
Subject: [PATCH 15/22] fix rebase issue
---
core/overlay/conversion.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/overlay/conversion.go b/core/overlay/conversion.go
index e538c58bcce0..89d8dc8079ca 100644
--- a/core/overlay/conversion.go
+++ b/core/overlay/conversion.go
@@ -36,7 +36,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils"
- "github.com/gballet/go-verkle"
+ "github.com/ethereum/go-verkle"
"github.com/holiman/uint256"
)
From a774d16436307c03ecb9af0b056342441b4f5adf Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 8 Feb 2024 17:55:15 +0100
Subject: [PATCH 16/22] fix a few rebase issues
---
core/chain_makers.go | 18 +++++++-----------
core/state_processor.go | 9 +++++++++
2 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 3909500d91fa..1c232e6b6d92 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -365,7 +365,7 @@ func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int,
return db, blocks, receipts
}
-func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) {
+func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, diskdb ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) {
if config == nil {
config = params.TestChainConfig
}
@@ -434,20 +434,16 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
return nil, nil
}
var snaps *snapshot.Tree
- triedb := state.NewDatabaseWithConfig(db, nil)
- triedb.StartVerkleTransition(common.Hash{}, common.Hash{}, config, config.PragueTime, common.Hash{})
- triedb.EndVerkleTransition()
- //statedb, err := state.New(parent.Root(), triedb, snaps)
- //if err != nil {
- // panic(fmt.Sprintf("could not find state for block %d: err=%v, parent root=%x", parent.NumberU64(), err, parent.Root()))
- //}
- statedb.Database().SaveTransitionState(parent.Root())
+ db := state.NewDatabaseWithConfig(diskdb, nil)
+ db.StartVerkleTransition(common.Hash{}, common.Hash{}, config, config.PragueTime, common.Hash{})
+ db.EndVerkleTransition()
+ db.SaveTransitionState(parent.Root())
for i := 0; i < n; i++ {
- // XXX merge uncommment
- statedb, err := state.New(parent.Root(), triedb, snaps)
+ statedb, err := state.New(parent.Root(), db, snaps)
if err != nil {
panic(fmt.Sprintf("could not find state for block %d: err=%v, parent root=%x", i, err, parent.Root()))
}
+ statedb.NewAccessWitness()
block, receipt := genblock(i, parent, statedb)
blocks[i] = block
receipts[i] = receipt
diff --git a/core/state_processor.go b/core/state_processor.go
index cb4d622d6d90..227608fb21b4 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/trie/utils"
)
// StateProcessor is a basic Processor, which takes care of transitioning
@@ -186,6 +187,14 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
}
+func InsertBlockHashHistoryAtEip2935Fork(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash, chain consensus.ChainHeaderReader) {
+ ancestor := chain.GetHeader(prevHash, prevNumber)
+ for i := prevNumber; i > 0 && i >= prevNumber-256; i-- {
+ ProcessParentBlockHash(statedb, i, ancestor.Hash())
+ ancestor = chain.GetHeader(ancestor.ParentHash, ancestor.Number.Uint64()-1)
+ }
+}
+
func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash) {
ringIndex := prevNumber % 256
var key common.Hash
From 17843378f5de6e2da1d5adc978db9eac46c15eb0 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Fri, 9 Feb 2024 11:08:48 +0100
Subject: [PATCH 17/22] add debug traces
---
core/blockchain.go | 2 ++
core/state/database.go | 5 +++++
eth/catalyst/api.go | 8 ++++----
3 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/core/blockchain.go b/core/blockchain.go
index 36e58ca8df57..8958b15754dc 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -1767,6 +1767,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
// is the fork block and that the conversion needs to be marked at started.
if !bc.stateCache.InTransition() && !bc.stateCache.Transitioned() {
bc.stateCache.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), bc.Config().PragueTime, parent.Root)
+ } else {
+ log.Debug("skipped initialization")
}
}
if parent.Number.Uint64() == conversionBlock {
diff --git a/core/state/database.go b/core/state/database.go
index ab714837d411..fa1af2a869e1 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -19,6 +19,7 @@ package state
import (
"errors"
"fmt"
+ "runtime/debug"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
@@ -237,6 +238,7 @@ func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.H
if pragueTime != nil {
chainConfig.PragueTime = pragueTime
}
+ debug.PrintStack()
}
func (db *cachingDB) ReorgThroughVerkleTransition() {
@@ -355,6 +357,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// If the verkle conversion has ended, return a single
// verkle trie.
if db.CurrentTransitionState.ended {
+ debug.PrintStack()
log.Debug("transition ended, returning a simple verkle tree")
return vkt, nil
}
@@ -566,6 +569,8 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
// as a verkle database.
ts, ok := db.TransitionStatePerRoot[root]
if !ok || ts == nil {
+ log.Debug("could not find any transition state, starting with a fresh state", "is verkle", db.triedb.IsVerkle())
+ debug.PrintStack()
// Start with a fresh state
ts = &TransitionState{ended: db.triedb.IsVerkle()}
}
diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go
index 864b3efe84f5..087a77015437 100644
--- a/eth/catalyst/api.go
+++ b/eth/catalyst/api.go
@@ -535,10 +535,10 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
api.eth.BlockChain().StartVerkleTransition(parent.Root, common.Hash{}, api.eth.BlockChain().Config(), nil, parent.Root)
}
}
- // Reset db merge state in case of a reorg
- if !api.eth.BlockChain().Config().IsPrague(block.Number(), block.Time()) {
- api.eth.BlockChain().ReorgThroughVerkleTransition()
- }
+ // // Reset db merge state in case of a reorg
+ // if !api.eth.BlockChain().Config().IsPrague(block.Number(), block.Time()) {
+ // api.eth.BlockChain().ReorgThroughVerkleTransition()
+ // }
// Another cornercase: if the node is in snap sync mode, but the CL client
// tries to make it import a block. That should be denied as pushing something
// into the database directly will conflict with the assumptions of snap sync
From ff3616838b4d315b5b47529f35e983d08c2345fa Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Fri, 9 Feb 2024 12:15:27 +0100
Subject: [PATCH 18/22] fix: assume that having to create a new transaction
state mean it hasn't happened.
This is a workaround, because it will always be false when restarting a node
that has started the transition but not necessarily completed it.
---
core/blockchain.go | 2 --
core/state/database.go | 6 +-----
eth/catalyst/api.go | 4 ----
3 files changed, 1 insertion(+), 11 deletions(-)
diff --git a/core/blockchain.go b/core/blockchain.go
index 8958b15754dc..36e58ca8df57 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -1767,8 +1767,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
// is the fork block and that the conversion needs to be marked at started.
if !bc.stateCache.InTransition() && !bc.stateCache.Transitioned() {
bc.stateCache.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), bc.Config().PragueTime, parent.Root)
- } else {
- log.Debug("skipped initialization")
}
}
if parent.Number.Uint64() == conversionBlock {
diff --git a/core/state/database.go b/core/state/database.go
index fa1af2a869e1..97facc29907f 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -19,7 +19,6 @@ package state
import (
"errors"
"fmt"
- "runtime/debug"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
@@ -238,7 +237,6 @@ func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.H
if pragueTime != nil {
chainConfig.PragueTime = pragueTime
}
- debug.PrintStack()
}
func (db *cachingDB) ReorgThroughVerkleTransition() {
@@ -357,7 +355,6 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// If the verkle conversion has ended, return a single
// verkle trie.
if db.CurrentTransitionState.ended {
- debug.PrintStack()
log.Debug("transition ended, returning a simple verkle tree")
return vkt, nil
}
@@ -570,9 +567,8 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
ts, ok := db.TransitionStatePerRoot[root]
if !ok || ts == nil {
log.Debug("could not find any transition state, starting with a fresh state", "is verkle", db.triedb.IsVerkle())
- debug.PrintStack()
// Start with a fresh state
- ts = &TransitionState{ended: db.triedb.IsVerkle()}
+ ts = &TransitionState{ended: false}
}
// Copy so that the CurrentAddress pointer in the map
diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go
index 087a77015437..925494a74d5f 100644
--- a/eth/catalyst/api.go
+++ b/eth/catalyst/api.go
@@ -535,10 +535,6 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
api.eth.BlockChain().StartVerkleTransition(parent.Root, common.Hash{}, api.eth.BlockChain().Config(), nil, parent.Root)
}
}
- // // Reset db merge state in case of a reorg
- // if !api.eth.BlockChain().Config().IsPrague(block.Number(), block.Time()) {
- // api.eth.BlockChain().ReorgThroughVerkleTransition()
- // }
// Another cornercase: if the node is in snap sync mode, but the CL client
// tries to make it import a block. That should be denied as pushing something
// into the database directly will conflict with the assumptions of snap sync
From a641947179c0f9ecabae2d499b6c9e988bea2fac Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 13 Feb 2024 07:58:37 +0100
Subject: [PATCH 19/22] add logs to debug shadowfork
---
core/state/database.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/core/state/database.go b/core/state/database.go
index 97facc29907f..075b948cf32e 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -552,7 +552,7 @@ func (db *cachingDB) SaveTransitionState(root common.Hash) {
// it has been saved.
db.TransitionStatePerRoot[root] = db.CurrentTransitionState.Copy()
- log.Debug("saving transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
+ fmt.Println("saving transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
}
}
@@ -566,7 +566,7 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
// as a verkle database.
ts, ok := db.TransitionStatePerRoot[root]
if !ok || ts == nil {
- log.Debug("could not find any transition state, starting with a fresh state", "is verkle", db.triedb.IsVerkle())
+ fmt.Println("could not find any transition state, starting with a fresh state", "is verkle", db.triedb.IsVerkle())
// Start with a fresh state
ts = &TransitionState{ended: false}
}
@@ -575,5 +575,5 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
// doesn't get overwritten.
db.CurrentTransitionState = ts.Copy()
- log.Debug("loaded transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
+ fmt.Println("loaded transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
}
From 7a3bb67881f307cae45437425910b3ca12ef0d3f Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 22 Feb 2024 16:03:51 +0100
Subject: [PATCH 20/22] persist conversion state to db and use an LRU cache for
active transition states (#375)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* persist conversion state to db
* fix: don't recreate LRU when writing state
* opt: only write state to db if not already present in LRU
* fix: rlp can't encode TransitionState
* fix: use gob because binary.Write does not support structs 🤦♂️
* fix: nil pointer panic
* add logs to debug shadowfork
* no such thing as not enough traces
* ditto
* fix stupid bug
* add a comment for readability
* add more traces
* Lock the state transition during conversion (#384)
* heavy handed approach: lock the state transition during conversion
* also lock transition state loading/unloading
* reduce logs verbosity
* add conversion test to workflow (#386)
* add conversion test to workflow
* mandatory -f switch fix in rm
* forgot & at the end of the geth command
* remove incorrect kill
* add debug traces
* add an overlay stride
* fix typo
* Apply suggestions from code review
---
.github/workflows/conversion.yml | 75 +++++++++++++++++++
consensus/beacon/consensus.go | 2 +
core/overlay/conversion.go | 7 +-
core/rawdb/accessors_overlay.go | 30 ++++++++
core/rawdb/schema.go | 7 ++
core/state/database.go | 119 ++++++++++++++++++++++---------
core/state_processor.go | 6 --
light/trie.go | 7 ++
8 files changed, 212 insertions(+), 41 deletions(-)
create mode 100644 .github/workflows/conversion.yml
create mode 100644 core/rawdb/accessors_overlay.go
diff --git a/.github/workflows/conversion.yml b/.github/workflows/conversion.yml
new file mode 100644
index 000000000000..40d4fed6925d
--- /dev/null
+++ b/.github/workflows/conversion.yml
@@ -0,0 +1,75 @@
+name: Overlay conversion
+
+on:
+ push:
+ branches: [ master, transition-post-genesis, store-transition-state-in-db ]
+ pull_request:
+ branches: [ master, kaustinen-with-shapella, transition-post-genesis, store-transition-state-in-db, lock-overlay-transition ]
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: self-hosted
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Go
+ uses: actions/setup-go@v2
+ with:
+ go-version: 1.21.1
+
+ - name: Cleanup from previous runs
+ run: |
+ rm -f log.txt
+ rm -rf .shadowfork
+ rm -f genesis.json
+
+ - name: Download genesis file
+ run: wget https://gist.githubusercontent.com/gballet/0b02a025428aa0e7b67941864d54716c/raw/bfb4e158bca5217b356a19b2ec55c4a45a7b2bad/genesis.json
+
+ - name: Init data
+ run: go run ./cmd/geth --dev --cache.preimages init genesis.json
+
+ - name: Run geth in devmode
+ run: go run ./cmd/geth --dev --dev.period=5 --cache.preimages --http --datadir=.shadowfork --override.overlay-stride=10 --override.prague=$(($(date +%s) + 45)) > log.txt &
+
+ - name: Wait for the transition to start
+ run: |
+ start_time=$(date +%s)
+ while true; do
+ sleep 5
+ current_time=$(date +%s)
+ elapsed_time=$((current_time - start_time))
+
+ # 2 minute timeout
+ if [ $elapsed_time -ge 120 ]; then
+ kill -9 $(pgrep -f geth)
+ exit 1
+ fi
+
+ # Check for signs that the conversion has started
+ if grep -q "Processing verkle conversion starting at" log.txt; then
+ break
+ fi
+ done
+
+ - name: Wait for the transition to end
+ run: |
+ start_time=$(date +%s)
+ while true; do
+ sleep 5
+ current_time=$(date +%s)
+ elapsed_time=$((current_time - start_time))
+
+ # 10 minute timeout
+ if [ $elapsed_time -ge 300 ]; then
+ cat log.txt
+ kill -9 $(pgrep -f geth)
+ exit 1
+ fi
+
+ # Check for signs that the conversion has started
+ if egrep -q "at block.*performing transition\? false" log.txt; then
+ kill -9 $(pgrep -f geth)
+ break
+ fi
+ done
diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go
index 7c93844f39c9..62d9f48feb2a 100644
--- a/consensus/beacon/consensus.go
+++ b/consensus/beacon/consensus.go
@@ -411,6 +411,8 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
return nil, fmt.Errorf("nil parent header for block %d", header.Number)
}
+ // Load transition state at beginning of block, because
+ // OpenTrie needs to know what the conversion status is.
state.Database().LoadTransitionState(parent.Root)
if chain.Config().ProofInBlocks {
diff --git a/core/overlay/conversion.go b/core/overlay/conversion.go
index 89d8dc8079ca..bf77ff0033f1 100644
--- a/core/overlay/conversion.go
+++ b/core/overlay/conversion.go
@@ -221,6 +221,8 @@ func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) er
// OverlayVerkleTransition contains the overlay conversion logic
func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedCount uint64) error {
migrdb := statedb.Database()
+ migrdb.LockCurrentTransitionState()
+ defer migrdb.UnLockCurrentTransitionState()
// verkle transition: if the conversion process is in progress, move
// N values from the MPT into the verkle tree.
@@ -289,7 +291,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
for count < maxMovedCount {
acc, err := types.FullAccount(accIt.Account())
if err != nil {
- log.Error("Invalid account encountered during traversal", "error", err)
+ fmt.Println("Invalid account encountered during traversal", "error", err)
return err
}
vkt.SetStorageRootConversion(*migrdb.GetCurrentAccountAddress(), acc.Root)
@@ -399,7 +401,6 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
return fmt.Errorf("account address len is zero is not 20: %d", len(addr))
}
}
- // fmt.Printf("account switch: %s != %s\n", crypto.Keccak256Hash(addr[:]), accIt.Hash())
if crypto.Keccak256Hash(addr[:]) != accIt.Hash() {
return fmt.Errorf("preimage file does not match account hash: %s != %s", crypto.Keccak256Hash(addr[:]), accIt.Hash())
}
@@ -416,7 +417,7 @@ func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedC
}
migrdb.SetCurrentPreimageOffset(preimageSeek)
- log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account", statedb.Database().GetCurrentAccountHash(), "storage processed", statedb.Database().GetStorageProcessed(), "last storage", statedb.Database().GetCurrentSlotHash())
+ log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account hash", statedb.Database().GetCurrentAccountHash(), "last account address", statedb.Database().GetCurrentAccountAddress(), "storage processed", statedb.Database().GetStorageProcessed(), "last storage", statedb.Database().GetCurrentSlotHash())
// Take all the collected key-values and prepare the new leaf values.
// This fires a background routine that will start doing the work that
diff --git a/core/rawdb/accessors_overlay.go b/core/rawdb/accessors_overlay.go
new file mode 100644
index 000000000000..5a371b9d307f
--- /dev/null
+++ b/core/rawdb/accessors_overlay.go
@@ -0,0 +1,30 @@
+// Copyright 2024 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rawdb
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
+)
+
+func ReadVerkleTransitionState(db ethdb.KeyValueReader, hash common.Hash) ([]byte, error) {
+ return db.Get(transitionStateKey(hash))
+}
+
+func WriteVerkleTransitionState(db ethdb.KeyValueWriter, hash common.Hash, state []byte) error {
+ return db.Put(transitionStateKey(hash), state)
+}
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
index 18722ed5d4cb..f7a3515715ae 100644
--- a/core/rawdb/schema.go
+++ b/core/rawdb/schema.go
@@ -122,6 +122,8 @@ var (
CliqueSnapshotPrefix = []byte("clique-")
+ VerkleTransitionStatePrefix = []byte("verkle-transition-state-")
+
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
)
@@ -250,6 +252,11 @@ func storageTrieNodeKey(accountHash common.Hash, path []byte) []byte {
return append(append(trieNodeStoragePrefix, accountHash.Bytes()...), path...)
}
+// transitionStateKey = transitionStatusKey + hash
+func transitionStateKey(hash common.Hash) []byte {
+ return append(VerkleTransitionStatePrefix, hash.Bytes()...)
+}
+
// IsLegacyTrieNode reports whether a provided database entry is a legacy trie
// node. The characteristics of legacy trie node are:
// - the key length is 32 bytes
diff --git a/core/state/database.go b/core/state/database.go
index 075b948cf32e..cca65bb6f993 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -17,8 +17,12 @@
package state
import (
+ "bytes"
+ "encoding/gob"
"errors"
"fmt"
+ "runtime/debug"
+ "sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
@@ -102,6 +106,10 @@ type Database interface {
SaveTransitionState(common.Hash)
LoadTransitionState(common.Hash)
+
+ LockCurrentTransitionState()
+
+ UnLockCurrentTransitionState()
}
// Trie is a Ethereum Merkle Patricia trie.
@@ -189,22 +197,24 @@ func NewDatabase(db ethdb.Database) Database {
// large memory cache.
func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
return &cachingDB{
- disk: db,
- codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
- codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
- triedb: trie.NewDatabaseWithConfig(db, config),
- addrToPoint: utils.NewPointCache(),
+ disk: db,
+ codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
+ codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
+ triedb: trie.NewDatabaseWithConfig(db, config),
+ addrToPoint: utils.NewPointCache(),
+ TransitionStatePerRoot: lru.NewBasicLRU[common.Hash, *TransitionState](100),
}
}
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
return &cachingDB{
- disk: db,
- codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
- codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
- triedb: triedb,
- addrToPoint: utils.NewPointCache(),
+ disk: db,
+ codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
+ codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
+ triedb: triedb,
+ addrToPoint: utils.NewPointCache(),
+ TransitionStatePerRoot: lru.NewBasicLRU[common.Hash, *TransitionState](100),
}
}
@@ -305,12 +315,12 @@ type cachingDB struct {
// TODO ensure that this info is in the DB
LastMerkleRoot common.Hash // root hash of the read-only base tree
CurrentTransitionState *TransitionState
- TransitionStatePerRoot map[common.Hash]*TransitionState
+ TransitionStatePerRoot lru.BasicLRU[common.Hash, *TransitionState]
+ transitionStateLock sync.Mutex
addrToPoint *utils.PointCache
baseRoot common.Hash // hash of the read-only base tree
-
}
func (db *cachingDB) openMPTTrie(root common.Hash) (Trie, error) {
@@ -543,37 +553,82 @@ func (db *cachingDB) SetLastMerkleRoot(merkleRoot common.Hash) {
}
func (db *cachingDB) SaveTransitionState(root common.Hash) {
- if db.TransitionStatePerRoot == nil {
- db.TransitionStatePerRoot = make(map[common.Hash]*TransitionState)
- }
-
+ db.transitionStateLock.Lock()
+ defer db.transitionStateLock.Unlock()
if db.CurrentTransitionState != nil {
- // Copy so that the address pointer isn't updated after
- // it has been saved.
- db.TransitionStatePerRoot[root] = db.CurrentTransitionState.Copy()
+ var buf bytes.Buffer
+ enc := gob.NewEncoder(&buf)
+ err := enc.Encode(db.CurrentTransitionState)
+ if err != nil {
+ log.Error("failed to encode transition state", "err", err)
+ return
+ }
+
+ if !db.TransitionStatePerRoot.Contains(root) {
+ // Copy so that the address pointer isn't updated after
+ // it has been saved.
+ db.TransitionStatePerRoot.Add(root, db.CurrentTransitionState.Copy())
+
+ rawdb.WriteVerkleTransitionState(db.DiskDB(), root, buf.Bytes())
+ }
- fmt.Println("saving transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
+ log.Debug("saving transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
}
}
func (db *cachingDB) LoadTransitionState(root common.Hash) {
- if db.TransitionStatePerRoot == nil {
- db.TransitionStatePerRoot = make(map[common.Hash]*TransitionState)
- }
+ db.transitionStateLock.Lock()
+ defer db.transitionStateLock.Unlock()
+ // Try to get the transition state from the cache and
+ // the DB if it's not there.
+ ts, ok := db.TransitionStatePerRoot.Get(root)
+ if !ok {
+ // Not in the cache, try getting it from the DB
+ data, err := rawdb.ReadVerkleTransitionState(db.DiskDB(), root)
+ if err != nil {
+ log.Error("failed to read transition state", "err", err)
+ return
+ }
+
+ // if a state could be read from the db, attempt to decode it
+ if len(data) > 0 {
+ var (
+ newts TransitionState
+ buf = bytes.NewBuffer(data[:])
+ dec = gob.NewDecoder(buf)
+ )
+ // Decode transition state
+ err = dec.Decode(&newts)
+ if err != nil {
+ log.Error("failed to decode transition state", "err", err)
+ return
+ }
+ ts = &newts
+ }
- // Initialize the first transition state, with the "ended"
- // field set to true if the database was created
- // as a verkle database.
- ts, ok := db.TransitionStatePerRoot[root]
- if !ok || ts == nil {
- fmt.Println("could not find any transition state, starting with a fresh state", "is verkle", db.triedb.IsVerkle())
- // Start with a fresh state
- ts = &TransitionState{ended: false}
+ // Fallback that should only happen before the transition
+ if ts == nil {
+ // Initialize the first transition state, with the "ended"
+ // field set to true if the database was created
+ // as a verkle database.
+ log.Debug("no transition state found, starting fresh", "is verkle", db.triedb.IsVerkle())
+ // Start with a fresh state
+ ts = &TransitionState{ended: db.triedb.IsVerkle()}
+ }
}
// Copy so that the CurrentAddress pointer in the map
// doesn't get overwritten.
db.CurrentTransitionState = ts.Copy()
- fmt.Println("loaded transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
+ log.Debug("loaded transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
+ debug.PrintStack()
+}
+
+func (db *cachingDB) LockCurrentTransitionState() {
+ db.transitionStateLock.Lock()
+}
+
+func (db *cachingDB) UnLockCurrentTransitionState() {
+ db.transitionStateLock.Unlock()
}
diff --git a/core/state_processor.go b/core/state_processor.go
index 227608fb21b4..2574120ce45f 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -107,12 +107,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return nil, nil, 0, errors.New("withdrawals before shanghai")
}
- // Perform the overlay transition, if relevant
- //parent := p.bc.GetHeaderByHash(header.ParentHash)
- //if err := OverlayVerkleTransition(statedb, parent.Root); err != nil {
- // return nil, nil, 0, fmt.Errorf("error performing verkle overlay transition: %w", err)
- //}
-
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), withdrawals)
diff --git a/light/trie.go b/light/trie.go
index df300c8c6ed2..7e7c03bc16c1 100644
--- a/light/trie.go
+++ b/light/trie.go
@@ -177,6 +177,13 @@ func (db *odrDatabase) LoadTransitionState(common.Hash) {
panic("not implemented") // TODO: Implement
}
+func (db *odrDatabase) LockCurrentTransitionState() {
+ panic("not implemented") // TODO: Implement
+}
+func (db *odrDatabase) UnLockCurrentTransitionState() {
+ panic("not implemented") // TODO: Implement
+}
+
type odrTrie struct {
db *odrDatabase
id *TrieID
From afc364be32732350ee8be7d75eb6e0cb7065ad9a Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Sat, 2 Mar 2024 08:00:37 +0100
Subject: [PATCH 21/22] eth: add debug_conversionStatus RPC call (#392)
* eth: add debug_conversionStatus RPC call
* add debug trace4s
* Apply suggestions from code review
* export started/ended fields
---
core/state/database.go | 36 ++++++++++++++++++------------------
eth/api_debug.go | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+), 18 deletions(-)
diff --git a/core/state/database.go b/core/state/database.go
index cca65bb6f993..5fa7738fb525 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -219,11 +219,11 @@ func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
}
func (db *cachingDB) InTransition() bool {
- return db.CurrentTransitionState != nil && db.CurrentTransitionState.started && !db.CurrentTransitionState.ended
+ return db.CurrentTransitionState != nil && db.CurrentTransitionState.Started && !db.CurrentTransitionState.Ended
}
func (db *cachingDB) Transitioned() bool {
- return db.CurrentTransitionState != nil && db.CurrentTransitionState.ended
+ return db.CurrentTransitionState != nil && db.CurrentTransitionState.Ended
}
// Fork implements the fork
@@ -236,7 +236,7 @@ func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.H
|____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ \/\_/ |__|___| /_____//_____/
|__|`)
db.CurrentTransitionState = &TransitionState{
- started: true,
+ Started: true,
// initialize so that the first storage-less accounts are processed
StorageProcessed: true,
}
@@ -255,15 +255,15 @@ func (db *cachingDB) ReorgThroughVerkleTransition() {
func (db *cachingDB) InitTransitionStatus(started, ended bool) {
db.CurrentTransitionState = &TransitionState{
- ended: ended,
- started: started,
+ Ended: ended,
+ Started: started,
// TODO add other fields when we handle mid-transition interrupts
}
}
func (db *cachingDB) EndVerkleTransition() {
- if !db.CurrentTransitionState.started {
- db.CurrentTransitionState.started = true
+ if !db.CurrentTransitionState.Started {
+ db.CurrentTransitionState.Started = true
}
fmt.Println(`
@@ -273,14 +273,14 @@ func (db *cachingDB) EndVerkleTransition() {
| | | Y \ ___/ \ ___/| |_\ ___/| |_> | Y \/ __ \| | | | | Y \/ __ \_\___ \ | |__/ __ \| | / /_/ \ ___// /_/ |
|____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ |____(____ |___| \____ |\___ \____ |
|__|`)
- db.CurrentTransitionState.ended = true
+ db.CurrentTransitionState.Ended = true
}
type TransitionState struct {
CurrentAccountAddress *common.Address // addresss of the last translated account
CurrentSlotHash common.Hash // hash of the last translated storage slot
CurrentPreimageOffset int64 // next byte to read from the preimage file
- started, ended bool
+ Started, Ended bool
// Mark whether the storage for an account has been processed. This is useful if the
// maximum number of leaves of the conversion is reached before the whole storage is
@@ -290,8 +290,8 @@ type TransitionState struct {
func (ts *TransitionState) Copy() *TransitionState {
ret := &TransitionState{
- started: ts.started,
- ended: ts.ended,
+ Started: ts.Started,
+ Ended: ts.Ended,
CurrentSlotHash: ts.CurrentSlotHash,
CurrentPreimageOffset: ts.CurrentPreimageOffset,
StorageProcessed: ts.StorageProcessed,
@@ -334,14 +334,14 @@ func (db *cachingDB) openMPTTrie(root common.Hash) (Trie, error) {
func (db *cachingDB) openVKTrie(root common.Hash) (Trie, error) {
payload, err := db.DiskDB().Get(trie.FlatDBVerkleNodeKeyPrefix)
if err != nil {
- return trie.NewVerkleTrie(verkle.New(), db.triedb, db.addrToPoint, db.CurrentTransitionState.ended), nil
+ return trie.NewVerkleTrie(verkle.New(), db.triedb, db.addrToPoint, db.CurrentTransitionState.Ended), nil
}
r, err := verkle.ParseNode(payload, 0)
if err != nil {
panic(err)
}
- return trie.NewVerkleTrie(r, db.triedb, db.addrToPoint, db.CurrentTransitionState.ended), err
+ return trie.NewVerkleTrie(r, db.triedb, db.addrToPoint, db.CurrentTransitionState.Ended), err
}
// OpenTrie opens the main account trie at a specific root hash.
@@ -364,7 +364,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
// If the verkle conversion has ended, return a single
// verkle trie.
- if db.CurrentTransitionState.ended {
+ if db.CurrentTransitionState.Ended {
log.Debug("transition ended, returning a simple verkle tree")
return vkt, nil
}
@@ -572,7 +572,7 @@ func (db *cachingDB) SaveTransitionState(root common.Hash) {
rawdb.WriteVerkleTransitionState(db.DiskDB(), root, buf.Bytes())
}
- log.Debug("saving transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
+ log.Debug("saving transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.Ended, "started", db.CurrentTransitionState.Started)
}
}
@@ -590,7 +590,7 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
return
}
- // if a state could be read from the db, attempt to decode it
+ // if a state could be read from the db, attempt to decode it
if len(data) > 0 {
var (
newts TransitionState
@@ -613,7 +613,7 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
// as a verkle database.
log.Debug("no transition state found, starting fresh", "is verkle", db.triedb.IsVerkle())
// Start with a fresh state
- ts = &TransitionState{ended: db.triedb.IsVerkle()}
+ ts = &TransitionState{Ended: db.triedb.IsVerkle()}
}
}
@@ -621,7 +621,7 @@ func (db *cachingDB) LoadTransitionState(root common.Hash) {
// doesn't get overwritten.
db.CurrentTransitionState = ts.Copy()
- log.Debug("loaded transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.ended, "started", db.CurrentTransitionState.started)
+ log.Debug("loaded transition state", "storage processed", db.CurrentTransitionState.StorageProcessed, "addr", db.CurrentTransitionState.CurrentAccountAddress, "slot hash", db.CurrentTransitionState.CurrentSlotHash, "root", root, "ended", db.CurrentTransitionState.Ended, "started", db.CurrentTransitionState.Started)
debug.PrintStack()
}
diff --git a/eth/api_debug.go b/eth/api_debug.go
index 9cfa9103fb58..3e0daac1b5b0 100644
--- a/eth/api_debug.go
+++ b/eth/api_debug.go
@@ -17,7 +17,9 @@
package eth
import (
+ "bytes"
"context"
+ "encoding/gob"
"errors"
"fmt"
"time"
@@ -432,3 +434,41 @@ func (api *DebugAPI) SetTrieFlushInterval(interval string) error {
func (api *DebugAPI) GetTrieFlushInterval() string {
return api.eth.blockchain.GetTrieFlushInterval().String()
}
+
+type ConversionStatusResult struct {
+ Started bool `json:"started"`
+ Ended bool `json:"ended"`
+}
+
+func (api *DebugAPI) ConversionStatus(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*ConversionStatusResult, error) {
+ block, err := api.eth.APIBackend.BlockByNumberOrHash(ctx, blockNrOrHash)
+ if err != nil {
+ return nil, err
+ }
+ data, err := rawdb.ReadVerkleTransitionState(api.eth.ChainDb(), block.Root())
+ if err != nil {
+ if err.Error() == "pebble: not found" {
+ return &ConversionStatusResult{}, nil
+ }
+ return nil, err
+ }
+ log.Info("found entry", "data", data)
+ if len(data) == 0 {
+ log.Info("found no data")
+ // started and ended will be false as no conversion has started
+ return &ConversionStatusResult{}, nil
+ }
+
+ var (
+ ts state.TransitionState
+ buf = bytes.NewBuffer(data[:])
+ dec = gob.NewDecoder(buf)
+ )
+ // Decode transition state
+ err = dec.Decode(&ts)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode transition state, err=%v", err)
+ }
+
+ return &ConversionStatusResult{Started: ts.Started, Ended: ts.Ended}, nil
+}
From bb3a39deb11fcdcb1d4023e3f82946c22f8d5399 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Wed, 13 Mar 2024 17:46:43 +0100
Subject: [PATCH 22/22] fix post-verge sync (#404)
* fix post-verge sync
* review: fix truncated comment
---
core/blockchain.go | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/core/blockchain.go b/core/blockchain.go
index 36e58ca8df57..9143fd6c699d 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -1768,6 +1768,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
if !bc.stateCache.InTransition() && !bc.stateCache.Transitioned() {
bc.stateCache.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), bc.Config().PragueTime, parent.Root)
}
+ } else {
+ // If the verkle activation time hasn't started, declare it as "not started".
+ // This is so that if the miner activates the conversion, the insertion happens
+ // in the correct mode.
+ bc.stateCache.InitTransitionStatus(false, false)
}
if parent.Number.Uint64() == conversionBlock {
bc.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), &parent.Time, parent.Root)