core: only compute state root once (#30299)

This PR refactors the genesis initialization a bit, s.th. we only
compute the blockhash once instead of twice as before (during hashAlloc
and flushAlloc)

This will significantly reduce the amount of memory allocated during
genesis init

---------

Co-authored-by: Gary Rong <garyrong0905@gmail.com>
pull/30305/head
Marius van der Wijden 3 months ago committed by GitHub
parent 2b9d198706
commit c686485a06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 46
      core/genesis.go

@ -145,13 +145,12 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
return statedb.Commit(0, false) return statedb.Commit(0, false)
} }
// flushAlloc is very similar with hash, but the main difference is all the generated // flushAlloc is very similar with hash, but the main difference is all the
// states will be persisted into the given database. Also, the genesis state // generated states will be persisted into the given database.
// specification will be flushed as well. func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Database) (common.Hash, error) {
func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Database, blockhash common.Hash) error {
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil) statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
if err != nil { if err != nil {
return err return common.Hash{}, err
} }
for addr, account := range *ga { for addr, account := range *ga {
if account.Balance != nil { if account.Balance != nil {
@ -167,21 +166,15 @@ func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Databa
} }
root, err := statedb.Commit(0, false) root, err := statedb.Commit(0, false)
if err != nil { if err != nil {
return err return common.Hash{}, err
} }
// Commit newly generated states into disk if it's not empty. // Commit newly generated states into disk if it's not empty.
if root != types.EmptyRootHash { if root != types.EmptyRootHash {
if err := triedb.Commit(root, true); err != nil { if err := triedb.Commit(root, true); err != nil {
return err return common.Hash{}, err
} }
} }
// Marshal the genesis state specification and persist. return root, nil
blob, err := json.Marshal(ga)
if err != nil {
return err
}
rawdb.WriteGenesisStateSpec(db, blockhash, blob)
return nil
} }
func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.GenesisAlloc, err error) { func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.GenesisAlloc, err error) {
@ -426,6 +419,11 @@ func (g *Genesis) ToBlock() *types.Block {
if err != nil { if err != nil {
panic(err) panic(err)
} }
return g.toBlockWithRoot(root)
}
// toBlockWithRoot constructs the genesis block with the given genesis state root.
func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
head := &types.Header{ head := &types.Header{
Number: new(big.Int).SetUint64(g.Number), Number: new(big.Int).SetUint64(g.Number),
Nonce: types.EncodeNonce(g.Nonce), Nonce: types.EncodeNonce(g.Nonce),
@ -482,8 +480,7 @@ func (g *Genesis) ToBlock() *types.Block {
// Commit writes the block and state of a genesis specification to the database. // Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block. // The block is committed as the canonical head block.
func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) { func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
block := g.ToBlock() if g.Number != 0 {
if block.Number().Sign() != 0 {
return nil, errors.New("can't commit genesis block with number > 0") return nil, errors.New("can't commit genesis block with number > 0")
} }
config := g.Config config := g.Config
@ -493,15 +490,22 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
if err := config.CheckConfigForkOrder(); err != nil { if err := config.CheckConfigForkOrder(); err != nil {
return nil, err return nil, err
} }
if config.Clique != nil && len(block.Extra()) < 32+crypto.SignatureLength { if config.Clique != nil && len(g.ExtraData) < 32+crypto.SignatureLength {
return nil, errors.New("can't start clique chain without signers") return nil, errors.New("can't start clique chain without signers")
} }
// All the checks has passed, flushAlloc the states derived from the genesis // flush the data to disk and compute the state root
// specification as well as the specification itself into the provided root, err := flushAlloc(&g.Alloc, db, triedb)
// database. if err != nil {
if err := flushAlloc(&g.Alloc, db, triedb, block.Hash()); err != nil { return nil, err
}
block := g.toBlockWithRoot(root)
// Marshal the genesis state specification and persist.
blob, err := json.Marshal(g.Alloc)
if err != nil {
return nil, err return nil, err
} }
rawdb.WriteGenesisStateSpec(db, block.Hash(), blob)
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty()) rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
rawdb.WriteBlock(db, block) rawdb.WriteBlock(db, block)
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil) rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)

Loading…
Cancel
Save