diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 9ab6335205f5..2179b61032b6 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -214,6 +214,7 @@ func initGenesis(ctx *cli.Context) error { } triedb := trie.NewDatabaseWithConfig(chaindb, &trie.Config{ Preimages: ctx.Bool(utils.CachePreimagesFlag.Name), + Verkle: genesis.IsVerkle(), }) _, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides) if err != nil { diff --git a/core/blockchain.go b/core/blockchain.go index 173613e2c5c2..e39cccad5b79 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -312,6 +312,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/genesis.go b/core/genesis.go index 74294afb7272..efa339024c7e 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. @@ -456,6 +459,12 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { } } +// IsVerkle indicates whether the state is already stored in a verkle +// tree at genesis time. +func (g *Genesis) IsVerkle() bool { + return g.Config.IsPrague(new(big.Int).SetUint64(g.Number), g.Timestamp) +} + // ToBlock returns the genesis block according to genesis specification. func (g *Genesis) ToBlock() *types.Block { root, err := g.Alloc.deriveHash(g.Config, g.Timestamp) @@ -530,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/state/database.go b/core/state/database.go index 342a90196b58..e58d3f848226 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -75,6 +75,8 @@ type Database interface { Transitioned() bool + InitTransitionStatus(bool, bool) + SetCurrentSlotHash(common.Hash) GetCurrentAccountAddress() *common.Address @@ -241,6 +243,14 @@ func (db *cachingDB) ReorgThroughVerkleTransition() { 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.CurrentTransitionState.started { db.CurrentTransitionState.started = true diff --git a/light/trie.go b/light/trie.go index d18f2a41ed52..df300c8c6ed2 100644 --- a/light/trie.go +++ b/light/trie.go @@ -121,6 +121,10 @@ func (db *odrDatabase) Transitioned() bool { panic("not implemented") // TODO: Implement } +func (db *odrDatabase) InitTransitionStatus(bool, bool) { + panic("not implemented") // TODO: Implement +} + func (db *odrDatabase) SetCurrentSlotHash(common.Hash) { panic("not implemented") // TODO: Implement }