cmd, core, miner: rework genesis setup (#30907)

This pull request refactors the genesis setup function, the major
changes are highlighted here:

**(a) Triedb is opened in verkle mode if `EnableVerkleAtGenesis` is
configured in chainConfig or the database has been initialized previously with
`EnableVerkleAtGenesis` configured**.

A new config field `EnableVerkleAtGenesis` has been added in the
chainConfig. This field must be configured with True if Geth wants to initialize 
the genesis in Verkle mode.

In the verkle devnet-7, the verkle transition is activated at genesis.
Therefore, the verkle rules should be used since the genesis. In production
networks (mainnet and public testnets), verkle activation always occurs after
the genesis block. Therefore, this flag is only made for devnet and should be
deprecated later. Besides, verkle transition at non-genesis block hasn't been
implemented yet, it should be done in the following PRs.

**(b) The genesis initialization condition has been simplified**
There is a special mode supported by the Geth is that: Geth can be
initialized with an existing chain segment, which can fasten the node sync
process by retaining the chain freezer folder.

Originally, if the triedb is regarded as uninitialized and the genesis block can
be found in the chain freezer, the genesis block along with genesis state will be
committed. This condition has been simplified to checking the presence of chain
config in key-value store. The existence of chain config can represent the genesis
has been committed.
This commit is contained in:
rjl493456442 2025-01-14 18:49:30 +08:00 committed by GitHub
parent 864e717b56
commit 37c0e6992e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 191 additions and 150 deletions

View File

@ -230,11 +230,10 @@ func initGenesis(ctx *cli.Context) error {
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle()) triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
defer triedb.Close() defer triedb.Close()
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides) _, hash, _, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
if err != nil { if err != nil {
utils.Fatalf("Failed to write genesis block: %v", err) utils.Fatalf("Failed to write genesis block: %v", err)
} }
log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash) log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash)
return nil return nil

View File

@ -170,7 +170,7 @@ func TestHistoryImportAndExport(t *testing.T) {
db2.Close() db2.Close()
}) })
genesis.MustCommit(db2, triedb.NewDatabase(db, triedb.HashDefaults)) genesis.MustCommit(db2, triedb.NewDatabase(db2, triedb.HashDefaults))
imported, err := core.NewBlockChain(db2, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil) imported, err := core.NewBlockChain(db2, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil)
if err != nil { if err != nil {
t.Fatalf("unable to initialize chain: %v", err) t.Fatalf("unable to initialize chain: %v", err)

View File

@ -269,14 +269,19 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
cacheConfig = defaultCacheConfig cacheConfig = defaultCacheConfig
} }
// Open trie database with provided config // Open trie database with provided config
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(genesis != nil && genesis.IsVerkle())) enableVerkle, err := EnableVerkleAtGenesis(db, genesis)
if err != nil {
return nil, err
}
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(enableVerkle))
// Setup the genesis block, commit the provided genesis specification // Write the supplied genesis to the database if it has not been initialized
// to database if the genesis block is not present yet, or load the // yet. The corresponding chain config will be returned, either from the
// stored one from database. // provided genesis or from the locally stored configuration if the genesis
chainConfig, genesisHash, genesisErr := SetupGenesisBlockWithOverride(db, triedb, genesis, overrides) // has already been initialized.
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { chainConfig, genesisHash, compatErr, err := SetupGenesisBlockWithOverride(db, triedb, genesis, overrides)
return nil, genesisErr if err != nil {
return nil, err
} }
log.Info("") log.Info("")
log.Info(strings.Repeat("-", 153)) log.Info(strings.Repeat("-", 153))
@ -303,7 +308,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
vmConfig: vmConfig, vmConfig: vmConfig,
logger: vmConfig.Tracer, logger: vmConfig.Tracer,
} }
var err error
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped) bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped)
if err != nil { if err != nil {
return nil, err return nil, err
@ -453,16 +457,15 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
} }
// Rewind the chain in case of an incompatible config upgrade. // Rewind the chain in case of an incompatible config upgrade.
if compat, ok := genesisErr.(*params.ConfigCompatError); ok { if compatErr != nil {
log.Warn("Rewinding chain to upgrade configuration", "err", compat) log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
if compat.RewindToTime > 0 { if compatErr.RewindToTime > 0 {
bc.SetHeadWithTimestamp(compat.RewindToTime) bc.SetHeadWithTimestamp(compatErr.RewindToTime)
} else { } else {
bc.SetHead(compat.RewindToBlock) bc.SetHead(compatErr.RewindToBlock)
} }
rawdb.WriteChainConfig(db, genesisHash, chainConfig) rawdb.WriteChainConfig(db, genesisHash, chainConfig)
} }
// Start tx indexer if it's enabled. // Start tx indexer if it's enabled.
if txLookupLimit != nil { if txLookupLimit != nil {
bc.txIndexer = newTxIndexer(*txLookupLimit, bc) bc.txIndexer = newTxIndexer(*txLookupLimit, bc)

View File

@ -247,6 +247,24 @@ type ChainOverrides struct {
OverrideVerkle *uint64 OverrideVerkle *uint64
} }
// apply applies the chain overrides on the supplied chain config.
func (o *ChainOverrides) apply(cfg *params.ChainConfig) (*params.ChainConfig, error) {
if o == nil || cfg == nil {
return cfg, nil
}
cpy := *cfg
if o.OverrideCancun != nil {
cpy.CancunTime = o.OverrideCancun
}
if o.OverrideVerkle != nil {
cpy.VerkleTime = o.OverrideVerkle
}
if err := cpy.CheckConfigForkOrder(); err != nil {
return nil, err
}
return &cpy, nil
}
// SetupGenesisBlock writes or updates the genesis block in db. // SetupGenesisBlock writes or updates the genesis block in db.
// The block that will be used is: // The block that will be used is:
// //
@ -258,109 +276,102 @@ type ChainOverrides struct {
// The stored chain configuration will be updated if it is compatible (i.e. does not // The stored chain configuration will be updated if it is compatible (i.e. does not
// specify a fork block below the local head block). In case of a conflict, the // specify a fork block below the local head block). In case of a conflict, the
// error is a *params.ConfigCompatError and the new, unwritten config is returned. // error is a *params.ConfigCompatError and the new, unwritten config is returned.
// func SetupGenesisBlock(db ethdb.Database, triedb *triedb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
// The returned chain configuration is never nil.
func SetupGenesisBlock(db ethdb.Database, triedb *triedb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlockWithOverride(db, triedb, genesis, nil) return SetupGenesisBlockWithOverride(db, triedb, genesis, nil)
} }
func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, genesis *Genesis, overrides *ChainOverrides) (*params.ChainConfig, common.Hash, error) { func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, genesis *Genesis, overrides *ChainOverrides) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
// Sanitize the supplied genesis, ensuring it has the associated chain
// config attached.
if genesis != nil && genesis.Config == nil { if genesis != nil && genesis.Config == nil {
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig return nil, common.Hash{}, nil, errGenesisNoConfig
} }
applyOverrides := func(config *params.ChainConfig) { // Commit the genesis if the database is empty
if config != nil { ghash := rawdb.ReadCanonicalHash(db, 0)
if overrides != nil && overrides.OverrideCancun != nil { if (ghash == common.Hash{}) {
config.CancunTime = overrides.OverrideCancun
}
if overrides != nil && overrides.OverrideVerkle != nil {
config.VerkleTime = overrides.OverrideVerkle
}
}
}
// Just commit the new block if there is no stored genesis block.
stored := rawdb.ReadCanonicalHash(db, 0)
if (stored == common.Hash{}) {
if genesis == nil { if genesis == nil {
log.Info("Writing default main-net genesis block") log.Info("Writing default main-net genesis block")
genesis = DefaultGenesisBlock() genesis = DefaultGenesisBlock()
} else { } else {
log.Info("Writing custom genesis block") log.Info("Writing custom genesis block")
} }
chainCfg, err := overrides.apply(genesis.Config)
if err != nil {
return nil, common.Hash{}, nil, err
}
genesis.Config = chainCfg
applyOverrides(genesis.Config)
block, err := genesis.Commit(db, triedb) block, err := genesis.Commit(db, triedb)
if err != nil { if err != nil {
return genesis.Config, common.Hash{}, err return nil, common.Hash{}, nil, err
} }
return genesis.Config, block.Hash(), nil return chainCfg, block.Hash(), nil, nil
} }
// The genesis block is present(perhaps in ancient database) while the // Commit the genesis if the genesis block exists in the ancient database
// state database is not initialized yet. It can happen that the node // but the key-value database is empty without initializing the genesis
// is initialized with an external ancient store. Commit genesis state // fields. This scenario can occur when the node is created from scratch
// in this case. // with an existing ancient store.
header := rawdb.ReadHeader(db, stored, 0) storedCfg := rawdb.ReadChainConfig(db, ghash)
if header.Root != types.EmptyRootHash && !triedb.Initialized(header.Root) { if storedCfg == nil {
// Ensure the stored genesis block matches with the given genesis. Private
// networks must explicitly specify the genesis in the config file, mainnet
// genesis will be used as default and the initialization will always fail.
if genesis == nil { if genesis == nil {
log.Info("Writing default main-net genesis block")
genesis = DefaultGenesisBlock() genesis = DefaultGenesisBlock()
} else {
log.Info("Writing custom genesis block")
} }
applyOverrides(genesis.Config) chainCfg, err := overrides.apply(genesis.Config)
// Ensure the stored genesis matches with the given one. if err != nil {
hash := genesis.ToBlock().Hash() return nil, common.Hash{}, nil, err
if hash != stored { }
return genesis.Config, hash, &GenesisMismatchError{stored, hash} genesis.Config = chainCfg
if hash := genesis.ToBlock().Hash(); hash != ghash {
return nil, common.Hash{}, nil, &GenesisMismatchError{ghash, hash}
} }
block, err := genesis.Commit(db, triedb) block, err := genesis.Commit(db, triedb)
if err != nil { if err != nil {
return genesis.Config, hash, err return nil, common.Hash{}, nil, err
} }
return genesis.Config, block.Hash(), nil return chainCfg, block.Hash(), nil, nil
} }
// Check whether the genesis block is already written. // The genesis block has already been committed previously. Verify that the
// provided genesis with chain overrides matches the existing one, and update
// the stored chain config if necessary.
if genesis != nil { if genesis != nil {
applyOverrides(genesis.Config) chainCfg, err := overrides.apply(genesis.Config)
hash := genesis.ToBlock().Hash() if err != nil {
if hash != stored { return nil, common.Hash{}, nil, err
return genesis.Config, hash, &GenesisMismatchError{stored, hash} }
genesis.Config = chainCfg
if hash := genesis.ToBlock().Hash(); hash != ghash {
return nil, common.Hash{}, nil, &GenesisMismatchError{ghash, hash}
} }
}
// Get the existing chain configuration.
newcfg := genesis.configOrDefault(stored)
applyOverrides(newcfg)
if err := newcfg.CheckConfigForkOrder(); err != nil {
return newcfg, common.Hash{}, err
}
storedcfg := rawdb.ReadChainConfig(db, stored)
if storedcfg == nil {
log.Warn("Found genesis block without chain config")
rawdb.WriteChainConfig(db, stored, newcfg)
return newcfg, stored, nil
}
storedData, _ := json.Marshal(storedcfg)
// Special case: if a private network is being used (no genesis and also no
// mainnet hash in the database), we must not apply the `configOrDefault`
// chain config as that would be AllProtocolChanges (applying any new fork
// on top of an existing private network genesis block). In that case, only
// apply the overrides.
if genesis == nil && stored != params.MainnetGenesisHash {
newcfg = storedcfg
applyOverrides(newcfg)
} }
// Check config compatibility and write the config. Compatibility errors // Check config compatibility and write the config. Compatibility errors
// are returned to the caller unless we're already at block zero. // are returned to the caller unless we're already at block zero.
head := rawdb.ReadHeadHeader(db) head := rawdb.ReadHeadHeader(db)
if head == nil { if head == nil {
return newcfg, stored, errors.New("missing head header") return nil, common.Hash{}, nil, errors.New("missing head header")
} }
compatErr := storedcfg.CheckCompatible(newcfg, head.Number.Uint64(), head.Time) newCfg := genesis.chainConfigOrDefault(ghash, storedCfg)
// TODO(rjl493456442) better to define the comparator of chain config
// and short circuit if the chain config is not changed.
compatErr := storedCfg.CheckCompatible(newCfg, head.Number.Uint64(), head.Time)
if compatErr != nil && ((head.Number.Uint64() != 0 && compatErr.RewindToBlock != 0) || (head.Time != 0 && compatErr.RewindToTime != 0)) { if compatErr != nil && ((head.Number.Uint64() != 0 && compatErr.RewindToBlock != 0) || (head.Time != 0 && compatErr.RewindToTime != 0)) {
return newcfg, stored, compatErr return newCfg, ghash, compatErr, nil
} }
// Don't overwrite if the old is identical to the new // Don't overwrite if the old is identical to the new. It's useful
if newData, _ := json.Marshal(newcfg); !bytes.Equal(storedData, newData) { // for the scenarios that database is opened in the read-only mode.
rawdb.WriteChainConfig(db, stored, newcfg) storedData, _ := json.Marshal(storedCfg)
if newData, _ := json.Marshal(newCfg); !bytes.Equal(storedData, newData) {
rawdb.WriteChainConfig(db, ghash, newCfg)
} }
return newcfg, stored, nil return newCfg, ghash, nil, nil
} }
// LoadChainConfig loads the stored chain config if it is already present in // LoadChainConfig loads the stored chain config if it is already present in
@ -396,7 +407,10 @@ func LoadChainConfig(db ethdb.Database, genesis *Genesis) (*params.ChainConfig,
return params.MainnetChainConfig, nil return params.MainnetChainConfig, nil
} }
func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { // chainConfigOrDefault retrieves the attached chain configuration. If the genesis
// object is null, it returns the default chain configuration based on the given
// genesis hash, or the locally stored config if it's not a pre-defined network.
func (g *Genesis) chainConfigOrDefault(ghash common.Hash, stored *params.ChainConfig) *params.ChainConfig {
switch { switch {
case g != nil: case g != nil:
return g.Config return g.Config
@ -407,14 +421,14 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
case ghash == params.SepoliaGenesisHash: case ghash == params.SepoliaGenesisHash:
return params.SepoliaChainConfig return params.SepoliaChainConfig
default: default:
return params.AllEthashProtocolChanges return stored
} }
} }
// IsVerkle indicates whether the state is already stored in a verkle // IsVerkle indicates whether the state is already stored in a verkle
// tree at genesis time. // tree at genesis time.
func (g *Genesis) IsVerkle() bool { func (g *Genesis) IsVerkle() bool {
return g.Config.IsVerkle(new(big.Int).SetUint64(g.Number), g.Timestamp) return g.Config.IsVerkleGenesis()
} }
// ToBlock returns the genesis block according to genesis specification. // ToBlock returns the genesis block according to genesis specification.
@ -494,7 +508,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
} }
config := g.Config config := g.Config
if config == nil { if config == nil {
config = params.AllEthashProtocolChanges return nil, errors.New("invalid genesis without chain config")
} }
if err := config.CheckConfigForkOrder(); err != nil { if err := config.CheckConfigForkOrder(); err != nil {
return nil, err return nil, err
@ -514,16 +528,17 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
if err != nil { if err != nil {
return nil, err return nil, err
} }
rawdb.WriteGenesisStateSpec(db, block.Hash(), blob) batch := db.NewBatch()
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty()) rawdb.WriteGenesisStateSpec(batch, block.Hash(), blob)
rawdb.WriteBlock(db, block) rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), block.Difficulty())
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil) rawdb.WriteBlock(batch, block)
rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64()) rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), nil)
rawdb.WriteHeadBlockHash(db, block.Hash()) rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
rawdb.WriteHeadFastBlockHash(db, block.Hash()) rawdb.WriteHeadBlockHash(batch, block.Hash())
rawdb.WriteHeadHeaderHash(db, block.Hash()) rawdb.WriteHeadFastBlockHash(batch, block.Hash())
rawdb.WriteChainConfig(db, block.Hash(), config) rawdb.WriteHeadHeaderHash(batch, block.Hash())
return block, nil rawdb.WriteChainConfig(batch, block.Hash(), config)
return block, batch.Write()
} }
// MustCommit writes the genesis block and state to db, panicking on error. // MustCommit writes the genesis block and state to db, panicking on error.
@ -536,6 +551,29 @@ func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types.
return block return block
} }
// EnableVerkleAtGenesis indicates whether the verkle fork should be activated
// at genesis. This is a temporary solution only for verkle devnet testing, where
// verkle fork is activated at genesis, and the configured activation date has
// already passed.
//
// In production networks (mainnet and public testnets), verkle activation always
// occurs after the genesis block, making this function irrelevant in those cases.
func EnableVerkleAtGenesis(db ethdb.Database, genesis *Genesis) (bool, error) {
if genesis != nil {
if genesis.Config == nil {
return false, errGenesisNoConfig
}
return genesis.Config.EnableVerkleAtGenesis, nil
}
if ghash := rawdb.ReadCanonicalHash(db, 0); ghash != (common.Hash{}) {
chainCfg := rawdb.ReadChainConfig(db, ghash)
if chainCfg != nil {
return chainCfg.EnableVerkleAtGenesis, nil
}
}
return false, nil
}
// DefaultGenesisBlock returns the Ethereum main net genesis block. // DefaultGenesisBlock returns the Ethereum main net genesis block.
func DefaultGenesisBlock() *Genesis { func DefaultGenesisBlock() *Genesis {
return &Genesis{ return &Genesis{

View File

@ -54,23 +54,23 @@ func testSetupGenesis(t *testing.T, scheme string) {
oldcustomg.Config = &params.ChainConfig{HomesteadBlock: big.NewInt(2)} oldcustomg.Config = &params.ChainConfig{HomesteadBlock: big.NewInt(2)}
tests := []struct { tests := []struct {
name string name string
fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error) fn func(ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error)
wantConfig *params.ChainConfig wantConfig *params.ChainConfig
wantHash common.Hash wantHash common.Hash
wantErr error wantErr error
wantCompactErr *params.ConfigCompatError
}{ }{
{ {
name: "genesis without ChainConfig", name: "genesis without ChainConfig",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), new(Genesis)) return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), new(Genesis))
}, },
wantErr: errGenesisNoConfig, wantErr: errGenesisNoConfig,
wantConfig: params.AllEthashProtocolChanges,
}, },
{ {
name: "no block in DB, genesis == nil", name: "no block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil) return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
}, },
wantHash: params.MainnetGenesisHash, wantHash: params.MainnetGenesisHash,
@ -78,7 +78,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
}, },
{ {
name: "mainnet block in DB, genesis == nil", name: "mainnet block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
DefaultGenesisBlock().MustCommit(db, triedb.NewDatabase(db, newDbConfig(scheme))) DefaultGenesisBlock().MustCommit(db, triedb.NewDatabase(db, newDbConfig(scheme)))
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil) return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
}, },
@ -87,7 +87,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
}, },
{ {
name: "custom block in DB, genesis == nil", name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
tdb := triedb.NewDatabase(db, newDbConfig(scheme)) tdb := triedb.NewDatabase(db, newDbConfig(scheme))
customg.Commit(db, tdb) customg.Commit(db, tdb)
return SetupGenesisBlock(db, tdb, nil) return SetupGenesisBlock(db, tdb, nil)
@ -97,18 +97,16 @@ func testSetupGenesis(t *testing.T, scheme string) {
}, },
{ {
name: "custom block in DB, genesis == sepolia", name: "custom block in DB, genesis == sepolia",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
tdb := triedb.NewDatabase(db, newDbConfig(scheme)) tdb := triedb.NewDatabase(db, newDbConfig(scheme))
customg.Commit(db, tdb) customg.Commit(db, tdb)
return SetupGenesisBlock(db, tdb, DefaultSepoliaGenesisBlock()) return SetupGenesisBlock(db, tdb, DefaultSepoliaGenesisBlock())
}, },
wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash}, wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash},
wantHash: params.SepoliaGenesisHash,
wantConfig: params.SepoliaChainConfig,
}, },
{ {
name: "compatible config in DB", name: "compatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
tdb := triedb.NewDatabase(db, newDbConfig(scheme)) tdb := triedb.NewDatabase(db, newDbConfig(scheme))
oldcustomg.Commit(db, tdb) oldcustomg.Commit(db, tdb)
return SetupGenesisBlock(db, tdb, &customg) return SetupGenesisBlock(db, tdb, &customg)
@ -118,7 +116,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
}, },
{ {
name: "incompatible config in DB", name: "incompatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
// Commit the 'old' genesis block with Homestead transition at #2. // Commit the 'old' genesis block with Homestead transition at #2.
// Advance to block #4, past the homestead transition block of customg. // Advance to block #4, past the homestead transition block of customg.
tdb := triedb.NewDatabase(db, newDbConfig(scheme)) tdb := triedb.NewDatabase(db, newDbConfig(scheme))
@ -135,7 +133,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
}, },
wantHash: customghash, wantHash: customghash,
wantConfig: customg.Config, wantConfig: customg.Config,
wantErr: &params.ConfigCompatError{ wantCompactErr: &params.ConfigCompatError{
What: "Homestead fork block", What: "Homestead fork block",
StoredBlock: big.NewInt(2), StoredBlock: big.NewInt(2),
NewBlock: big.NewInt(3), NewBlock: big.NewInt(3),
@ -146,12 +144,16 @@ func testSetupGenesis(t *testing.T, scheme string) {
for _, test := range tests { for _, test := range tests {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
config, hash, err := test.fn(db) config, hash, compatErr, err := test.fn(db)
// Check the return values. // Check the return values.
if !reflect.DeepEqual(err, test.wantErr) { if !reflect.DeepEqual(err, test.wantErr) {
spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true} spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true}
t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr)) t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr))
} }
if !reflect.DeepEqual(compatErr, test.wantCompactErr) {
spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true}
t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(compatErr), spew.NewFormatter(test.wantCompactErr))
}
if !reflect.DeepEqual(config, test.wantConfig) { if !reflect.DeepEqual(config, test.wantConfig) {
t.Errorf("%s:\nreturned %v\nwant %v", test.name, config, test.wantConfig) t.Errorf("%s:\nreturned %v\nwant %v", test.name, config, test.wantConfig)
} }
@ -279,6 +281,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
PragueTime: &verkleTime, PragueTime: &verkleTime,
VerkleTime: &verkleTime, VerkleTime: &verkleTime,
TerminalTotalDifficulty: big.NewInt(0), TerminalTotalDifficulty: big.NewInt(0),
EnableVerkleAtGenesis: true,
Ethash: nil, Ethash: nil,
Clique: nil, Clique: nil,
} }

View File

@ -57,6 +57,7 @@ var (
ShanghaiTime: u64(0), ShanghaiTime: u64(0),
VerkleTime: u64(0), VerkleTime: u64(0),
TerminalTotalDifficulty: common.Big0, TerminalTotalDifficulty: common.Big0,
EnableVerkleAtGenesis: true,
// TODO uncomment when proof generation is merged // TODO uncomment when proof generation is merged
// ProofInBlocks: true, // ProofInBlocks: true,
} }
@ -77,6 +78,7 @@ var (
ShanghaiTime: u64(0), ShanghaiTime: u64(0),
VerkleTime: u64(0), VerkleTime: u64(0),
TerminalTotalDifficulty: common.Big0, TerminalTotalDifficulty: common.Big0,
EnableVerkleAtGenesis: true,
} }
) )

View File

@ -145,7 +145,7 @@ func createMiner(t *testing.T) *Miner {
chainDB := rawdb.NewMemoryDatabase() chainDB := rawdb.NewMemoryDatabase()
triedb := triedb.NewDatabase(chainDB, nil) triedb := triedb.NewDatabase(chainDB, nil)
genesis := minerTestGenesisBlock(15, 11_500_000, common.HexToAddress("12345")) genesis := minerTestGenesisBlock(15, 11_500_000, common.HexToAddress("12345"))
chainConfig, _, err := core.SetupGenesisBlock(chainDB, triedb, genesis) chainConfig, _, _, err := core.SetupGenesisBlock(chainDB, triedb, genesis)
if err != nil { if err != nil {
t.Fatalf("can't create new chain config: %v", err) t.Fatalf("can't create new chain config: %v", err)
} }

View File

@ -326,6 +326,19 @@ type ChainConfig struct {
DepositContractAddress common.Address `json:"depositContractAddress,omitempty"` DepositContractAddress common.Address `json:"depositContractAddress,omitempty"`
// EnableVerkleAtGenesis is a flag that specifies whether the network uses
// the Verkle tree starting from the genesis block. If set to true, the
// genesis state will be committed using the Verkle tree, eliminating the
// need for any Verkle transition later.
//
// This is a temporary flag only for verkle devnet testing, where verkle is
// activated at genesis, and the configured activation date has already passed.
//
// In production networks (mainnet and public testnets), verkle activation
// always occurs after the genesis block, making this flag irrelevant in
// those cases.
EnableVerkleAtGenesis bool `json:"enableVerkleAtGenesis,omitempty"`
// Various consensus engines // Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"` Ethash *EthashConfig `json:"ethash,omitempty"`
Clique *CliqueConfig `json:"clique,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"`
@ -525,6 +538,20 @@ func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time)
} }
// IsVerkleGenesis checks whether the verkle fork is activated at the genesis block.
//
// Verkle mode is considered enabled if the verkle fork time is configured,
// regardless of whether the local time has surpassed the fork activation time.
// This is a temporary workaround for verkle devnet testing, where verkle is
// activated at genesis, and the configured activation date has already passed.
//
// In production networks (mainnet and public testnets), verkle activation
// always occurs after the genesis block, making this function irrelevant in
// those cases.
func (c *ChainConfig) IsVerkleGenesis() bool {
return c.EnableVerkleAtGenesis
}
// IsEIP4762 returns whether eip 4762 has been activated at given block. // IsEIP4762 returns whether eip 4762 has been activated at given block.
func (c *ChainConfig) IsEIP4762(num *big.Int, time uint64) bool { func (c *ChainConfig) IsEIP4762(num *big.Int, time uint64) bool {
return c.IsVerkle(num, time) return c.IsVerkle(num, time)

View File

@ -64,10 +64,6 @@ type backend interface {
// state. An error will be returned if the specified state is not available. // state. An error will be returned if the specified state is not available.
StateReader(root common.Hash) (database.StateReader, error) StateReader(root common.Hash) (database.StateReader, error)
// Initialized returns an indicator if the state data is already initialized
// according to the state scheme.
Initialized(genesisRoot common.Hash) bool
// Size returns the current storage size of the diff layers on top of the // Size returns the current storage size of the diff layers on top of the
// disk layer and the storage size of the nodes cached in the disk layer. // disk layer and the storage size of the nodes cached in the disk layer.
// //
@ -178,12 +174,6 @@ func (db *Database) Size() (common.StorageSize, common.StorageSize, common.Stora
return diffs, nodes, preimages return diffs, nodes, preimages
} }
// Initialized returns an indicator if the state data is already initialized
// according to the state scheme.
func (db *Database) Initialized(genesisRoot common.Hash) bool {
return db.backend.Initialized(genesisRoot)
}
// Scheme returns the node scheme used in the database. // Scheme returns the node scheme used in the database.
func (db *Database) Scheme() string { func (db *Database) Scheme() string {
if db.config.PathDB != nil { if db.config.PathDB != nil {

View File

@ -532,12 +532,6 @@ func (c *cleaner) Delete(key []byte) error {
panic("not implemented") panic("not implemented")
} }
// Initialized returns an indicator if state data is already initialized
// in hash-based scheme by checking the presence of genesis state.
func (db *Database) Initialized(genesisRoot common.Hash) bool {
return rawdb.HasLegacyTrieNode(db.diskdb, genesisRoot)
}
// Update inserts the dirty nodes in provided nodeset into database and link the // Update inserts the dirty nodes in provided nodeset into database and link the
// account trie with multiple storage tries if necessary. // account trie with multiple storage tries if necessary.
func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet) error { func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet) error {

View File

@ -529,21 +529,6 @@ func (db *Database) Size() (diffs common.StorageSize, nodes common.StorageSize)
return diffs, nodes return diffs, nodes
} }
// Initialized returns an indicator if the state data is already
// initialized in path-based scheme.
func (db *Database) Initialized(genesisRoot common.Hash) bool {
var inited bool
db.tree.forEach(func(layer layer) {
if layer.rootHash() != types.EmptyRootHash {
inited = true
}
})
if !inited {
inited = rawdb.ReadSnapSyncStatusFlag(db.diskdb) != rawdb.StateSyncUnknown
}
return inited
}
// modifyAllowed returns the indicator if mutation is allowed. This function // modifyAllowed returns the indicator if mutation is allowed. This function
// assumes the db.lock is already held. // assumes the db.lock is already held.
func (db *Database) modifyAllowed() error { func (db *Database) modifyAllowed() error {