core: store genesis allocation and recommit them if necessary (#24460)
* core: store genesis allocation and recommit them if necessary * core: recover predefined genesis allocation if possible
This commit is contained in:
@@ -80,6 +80,81 @@ func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// flush adds allocated genesis accounts into a fresh new statedb and
|
||||
// commit the state changes into the given database handler.
|
||||
func (ga *GenesisAlloc) flush(db ethdb.Database) (common.Hash, error) {
|
||||
statedb, err := state.New(common.Hash{}, state.NewDatabase(db), nil)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
for addr, account := range *ga {
|
||||
statedb.AddBalance(addr, account.Balance)
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
for key, value := range account.Storage {
|
||||
statedb.SetState(addr, key, value)
|
||||
}
|
||||
}
|
||||
root, err := statedb.Commit(false)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
err = statedb.Database().TrieDB().Commit(root, true, nil)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return root, nil
|
||||
}
|
||||
|
||||
// write writes the json marshaled genesis state into database
|
||||
// with the given block hash as the unique identifier.
|
||||
func (ga *GenesisAlloc) write(db ethdb.KeyValueWriter, hash common.Hash) error {
|
||||
blob, err := json.Marshal(ga)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawdb.WriteGenesisState(db, hash, blob)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommitGenesisState loads the stored genesis state with the given block
|
||||
// hash and commits them into the given database handler.
|
||||
func CommitGenesisState(db ethdb.Database, hash common.Hash) error {
|
||||
var alloc GenesisAlloc
|
||||
blob := rawdb.ReadGenesisState(db, hash)
|
||||
if len(blob) != 0 {
|
||||
if err := alloc.UnmarshalJSON(blob); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Genesis allocation is missing and there are several possibilities:
|
||||
// the node is legacy which doesn't persist the genesis allocation or
|
||||
// the persisted allocation is just lost.
|
||||
// - supported networks(mainnet, testnets), recover with defined allocations
|
||||
// - private network, can't recover
|
||||
var genesis *Genesis
|
||||
switch hash {
|
||||
case params.MainnetGenesisHash:
|
||||
genesis = DefaultGenesisBlock()
|
||||
case params.RopstenGenesisHash:
|
||||
genesis = DefaultRopstenGenesisBlock()
|
||||
case params.RinkebyGenesisHash:
|
||||
genesis = DefaultRinkebyGenesisBlock()
|
||||
case params.GoerliGenesisHash:
|
||||
genesis = DefaultGoerliGenesisBlock()
|
||||
case params.SepoliaGenesisHash:
|
||||
genesis = DefaultSepoliaGenesisBlock()
|
||||
}
|
||||
if genesis != nil {
|
||||
alloc = genesis.Alloc
|
||||
} else {
|
||||
return errors.New("not found")
|
||||
}
|
||||
}
|
||||
_, err := alloc.flush(db)
|
||||
return err
|
||||
}
|
||||
|
||||
// GenesisAccount is an account in the state of the genesis block.
|
||||
type GenesisAccount struct {
|
||||
Code []byte `json:"code,omitempty"`
|
||||
@@ -267,19 +342,10 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||
if db == nil {
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
}
|
||||
statedb, err := state.New(common.Hash{}, state.NewDatabase(db), nil)
|
||||
root, err := g.Alloc.flush(db)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for addr, account := range g.Alloc {
|
||||
statedb.AddBalance(addr, account.Balance)
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
for key, value := range account.Storage {
|
||||
statedb.SetState(addr, key, value)
|
||||
}
|
||||
}
|
||||
root := statedb.IntermediateRoot(false)
|
||||
head := &types.Header{
|
||||
Number: new(big.Int).SetUint64(g.Number),
|
||||
Nonce: types.EncodeNonce(g.Nonce),
|
||||
@@ -307,9 +373,6 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||
head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee)
|
||||
}
|
||||
}
|
||||
statedb.Commit(false)
|
||||
statedb.Database().TrieDB().Commit(root, true, nil)
|
||||
|
||||
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil))
|
||||
}
|
||||
|
||||
@@ -330,6 +393,9 @@ func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
|
||||
if config.Clique != nil && len(block.Extra()) == 0 {
|
||||
return nil, errors.New("can't start clique chain without signers")
|
||||
}
|
||||
if err := g.Alloc.write(db, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
||||
rawdb.WriteBlock(db, block)
|
||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
||||
|
Reference in New Issue
Block a user