cmd, core: resolve scheme from a read-write database (#28313)
* cmd, core: resolve scheme from a read-write database * cmd, core, eth: move the scheme check in the ethereum constructor * cmd/geth: dump should in ro mode * cmd: reverts
This commit is contained in:
parent
13d1d425ac
commit
eeb5dc3ccf
@ -474,7 +474,7 @@ func dump(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
triedb := utils.MakeTrieDatabase(ctx, db, true, false) // always enable preimage lookup
|
triedb := utils.MakeTrieDatabase(ctx, db, true, true) // always enable preimage lookup
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||||
|
@ -268,7 +268,6 @@ var (
|
|||||||
StateSchemeFlag = &cli.StringFlag{
|
StateSchemeFlag = &cli.StringFlag{
|
||||||
Name: "state.scheme",
|
Name: "state.scheme",
|
||||||
Usage: "Scheme to use for storing ethereum state ('hash' or 'path')",
|
Usage: "Scheme to use for storing ethereum state ('hash' or 'path')",
|
||||||
Value: rawdb.HashScheme,
|
|
||||||
Category: flags.StateCategory,
|
Category: flags.StateCategory,
|
||||||
}
|
}
|
||||||
StateHistoryFlag = &cli.Uint64Flag{
|
StateHistoryFlag = &cli.Uint64Flag{
|
||||||
@ -1721,15 +1720,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||||||
if ctx.IsSet(StateHistoryFlag.Name) {
|
if ctx.IsSet(StateHistoryFlag.Name) {
|
||||||
cfg.StateHistory = ctx.Uint64(StateHistoryFlag.Name)
|
cfg.StateHistory = ctx.Uint64(StateHistoryFlag.Name)
|
||||||
}
|
}
|
||||||
// Parse state scheme, abort the process if it's not compatible.
|
if ctx.IsSet(StateSchemeFlag.Name) {
|
||||||
chaindb := tryMakeReadOnlyDatabase(ctx, stack)
|
cfg.StateScheme = ctx.String(StateSchemeFlag.Name)
|
||||||
scheme, err := ParseStateScheme(ctx, chaindb)
|
|
||||||
chaindb.Close()
|
|
||||||
if err != nil {
|
|
||||||
Fatalf("%v", err)
|
|
||||||
}
|
}
|
||||||
cfg.StateScheme = scheme
|
|
||||||
|
|
||||||
// Parse transaction history flag, if user is still using legacy config
|
// Parse transaction history flag, if user is still using legacy config
|
||||||
// file with 'TxLookupLimit' configured, copy the value to 'TransactionHistory'.
|
// file with 'TxLookupLimit' configured, copy the value to 'TransactionHistory'.
|
||||||
if cfg.TransactionHistory == ethconfig.Defaults.TransactionHistory && cfg.TxLookupLimit != ethconfig.Defaults.TxLookupLimit {
|
if cfg.TransactionHistory == ethconfig.Defaults.TransactionHistory && cfg.TxLookupLimit != ethconfig.Defaults.TxLookupLimit {
|
||||||
@ -2165,7 +2158,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||||||
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
|
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
|
||||||
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
|
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
|
||||||
}
|
}
|
||||||
scheme, err := ParseStateScheme(ctx, chainDb)
|
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), chainDb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("%v", err)
|
Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
@ -2224,47 +2217,12 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
|
|||||||
return preloads
|
return preloads
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseStateScheme resolves scheme identifier from CLI flag. If the provided
|
|
||||||
// state scheme is not compatible with the one of persistent scheme, an error
|
|
||||||
// will be returned.
|
|
||||||
//
|
|
||||||
// - none: use the scheme consistent with persistent state, or fallback
|
|
||||||
// to hash-based scheme if state is empty.
|
|
||||||
// - hash: use hash-based scheme or error out if not compatible with
|
|
||||||
// persistent state scheme.
|
|
||||||
// - path: use path-based scheme or error out if not compatible with
|
|
||||||
// persistent state scheme.
|
|
||||||
func ParseStateScheme(ctx *cli.Context, disk ethdb.Database) (string, error) {
|
|
||||||
// If state scheme is not specified, use the scheme consistent
|
|
||||||
// with persistent state, or fallback to hash mode if database
|
|
||||||
// is empty.
|
|
||||||
stored := rawdb.ReadStateScheme(disk)
|
|
||||||
if !ctx.IsSet(StateSchemeFlag.Name) {
|
|
||||||
if stored == "" {
|
|
||||||
// use default scheme for empty database, flip it when
|
|
||||||
// path mode is chosen as default
|
|
||||||
log.Info("State schema set to default", "scheme", "hash")
|
|
||||||
return rawdb.HashScheme, nil
|
|
||||||
}
|
|
||||||
log.Info("State scheme set to already existing", "scheme", stored)
|
|
||||||
return stored, nil // reuse scheme of persistent scheme
|
|
||||||
}
|
|
||||||
// If state scheme is specified, ensure it's compatible with
|
|
||||||
// persistent state.
|
|
||||||
scheme := ctx.String(StateSchemeFlag.Name)
|
|
||||||
if stored == "" || scheme == stored {
|
|
||||||
log.Info("State scheme set by user", "scheme", scheme)
|
|
||||||
return scheme, nil
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("incompatible state scheme, stored: %s, provided: %s", stored, scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
||||||
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool) *trie.Database {
|
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool) *trie.Database {
|
||||||
config := &trie.Config{
|
config := &trie.Config{
|
||||||
Preimages: preimage,
|
Preimages: preimage,
|
||||||
}
|
}
|
||||||
scheme, err := ParseStateScheme(ctx, disk)
|
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("%v", err)
|
Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
|
@ -120,8 +120,8 @@ func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deriveHash computes the state root according to the genesis specification.
|
// hash computes the state root according to the genesis specification.
|
||||||
func (ga *GenesisAlloc) deriveHash() (common.Hash, error) {
|
func (ga *GenesisAlloc) hash() (common.Hash, error) {
|
||||||
// Create an ephemeral in-memory database for computing hash,
|
// Create an ephemeral in-memory database for computing hash,
|
||||||
// all the derived states will be discarded to not pollute disk.
|
// all the derived states will be discarded to not pollute disk.
|
||||||
db := state.NewDatabase(rawdb.NewMemoryDatabase())
|
db := state.NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
@ -142,9 +142,9 @@ func (ga *GenesisAlloc) deriveHash() (common.Hash, error) {
|
|||||||
return statedb.Commit(0, false)
|
return statedb.Commit(0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush is very similar with deriveHash, but the main difference is
|
// flush is very similar with hash, but the main difference is all the generated
|
||||||
// all the generated states will be persisted into the given database.
|
// states will be persisted into the given database. Also, the genesis state
|
||||||
// Also, the genesis state specification will be flushed as well.
|
// specification will be flushed as well.
|
||||||
func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
|
func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.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 {
|
||||||
@ -179,39 +179,6 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitGenesisState loads the stored genesis state with the given block
|
|
||||||
// hash and commits it into the provided trie database.
|
|
||||||
func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
|
|
||||||
var alloc GenesisAlloc
|
|
||||||
blob := rawdb.ReadGenesisStateSpec(db, blockhash)
|
|
||||||
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 blockhash {
|
|
||||||
case params.MainnetGenesisHash:
|
|
||||||
genesis = DefaultGenesisBlock()
|
|
||||||
case params.GoerliGenesisHash:
|
|
||||||
genesis = DefaultGoerliGenesisBlock()
|
|
||||||
case params.SepoliaGenesisHash:
|
|
||||||
genesis = DefaultSepoliaGenesisBlock()
|
|
||||||
}
|
|
||||||
if genesis != nil {
|
|
||||||
alloc = genesis.Alloc
|
|
||||||
} else {
|
|
||||||
return errors.New("not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return alloc.flush(db, triedb, blockhash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenesisAccount is an account in the state of the genesis block.
|
// GenesisAccount is an account in the state of the genesis block.
|
||||||
type GenesisAccount struct {
|
type GenesisAccount struct {
|
||||||
Code []byte `json:"code,omitempty"`
|
Code []byte `json:"code,omitempty"`
|
||||||
@ -444,7 +411,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
|||||||
|
|
||||||
// ToBlock returns the genesis block according to genesis specification.
|
// ToBlock returns the genesis block according to genesis specification.
|
||||||
func (g *Genesis) ToBlock() *types.Block {
|
func (g *Genesis) ToBlock() *types.Block {
|
||||||
root, err := g.Alloc.deriveHash()
|
root, err := g.Alloc.hash()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -231,7 +231,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
|
|||||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||||
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
|
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
|
||||||
}
|
}
|
||||||
hash, _ = alloc.deriveHash()
|
hash, _ = alloc.hash()
|
||||||
)
|
)
|
||||||
blob, _ := json.Marshal(alloc)
|
blob, _ := json.Marshal(alloc)
|
||||||
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
||||||
|
@ -305,3 +305,38 @@ func ReadStateScheme(db ethdb.Reader) string {
|
|||||||
}
|
}
|
||||||
return HashScheme
|
return HashScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseStateScheme checks if the specified state scheme is compatible with
|
||||||
|
// the stored state.
|
||||||
|
//
|
||||||
|
// - If the provided scheme is none, use the scheme consistent with persistent
|
||||||
|
// state, or fallback to hash-based scheme if state is empty.
|
||||||
|
//
|
||||||
|
// - If the provided scheme is hash, use hash-based scheme or error out if not
|
||||||
|
// compatible with persistent state scheme.
|
||||||
|
//
|
||||||
|
// - If the provided scheme is path: use path-based scheme or error out if not
|
||||||
|
// compatible with persistent state scheme.
|
||||||
|
func ParseStateScheme(provided string, disk ethdb.Database) (string, error) {
|
||||||
|
// If state scheme is not specified, use the scheme consistent
|
||||||
|
// with persistent state, or fallback to hash mode if database
|
||||||
|
// is empty.
|
||||||
|
stored := ReadStateScheme(disk)
|
||||||
|
if provided == "" {
|
||||||
|
if stored == "" {
|
||||||
|
// use default scheme for empty database, flip it when
|
||||||
|
// path mode is chosen as default
|
||||||
|
log.Info("State schema set to default", "scheme", "hash")
|
||||||
|
return HashScheme, nil
|
||||||
|
}
|
||||||
|
log.Info("State scheme set to already existing", "scheme", stored)
|
||||||
|
return stored, nil // reuse scheme of persistent scheme
|
||||||
|
}
|
||||||
|
// If state scheme is specified, ensure it's compatible with
|
||||||
|
// persistent state.
|
||||||
|
if stored == "" || provided == stored {
|
||||||
|
log.Info("State scheme set by user", "scheme", provided)
|
||||||
|
return provided, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("incompatible state scheme, stored: %s, provided: %s", stored, provided)
|
||||||
|
}
|
||||||
|
@ -133,8 +133,12 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
scheme, err := rawdb.ParseStateScheme(config.StateScheme, chainDb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// Try to recover offline state pruning only in hash-based.
|
// Try to recover offline state pruning only in hash-based.
|
||||||
if config.StateScheme == rawdb.HashScheme {
|
if scheme == rawdb.HashScheme {
|
||||||
if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb); err != nil {
|
if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb); err != nil {
|
||||||
log.Error("Failed to recover state", "error", err)
|
log.Error("Failed to recover state", "error", err)
|
||||||
}
|
}
|
||||||
@ -194,7 +198,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||||||
SnapshotLimit: config.SnapshotCache,
|
SnapshotLimit: config.SnapshotCache,
|
||||||
Preimages: config.Preimages,
|
Preimages: config.Preimages,
|
||||||
StateHistory: config.StateHistory,
|
StateHistory: config.StateHistory,
|
||||||
StateScheme: config.StateScheme,
|
StateScheme: scheme,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// Override the chain config with provided settings.
|
// Override the chain config with provided settings.
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
||||||
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
|
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
@ -64,7 +63,6 @@ var Defaults = Config{
|
|||||||
TxLookupLimit: 2350000,
|
TxLookupLimit: 2350000,
|
||||||
TransactionHistory: 2350000,
|
TransactionHistory: 2350000,
|
||||||
StateHistory: params.FullImmutabilityThreshold,
|
StateHistory: params.FullImmutabilityThreshold,
|
||||||
StateScheme: rawdb.HashScheme,
|
|
||||||
LightPeers: 100,
|
LightPeers: 100,
|
||||||
DatabaseCache: 512,
|
DatabaseCache: 512,
|
||||||
TrieCleanCache: 154,
|
TrieCleanCache: 154,
|
||||||
@ -105,7 +103,11 @@ type Config struct {
|
|||||||
TxLookupLimit uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
|
TxLookupLimit uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
|
||||||
TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
|
TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
|
||||||
StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved.
|
StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved.
|
||||||
StateScheme string `toml:",omitempty"` // State scheme used to store ethereum state and merkle trie nodes on top
|
|
||||||
|
// State scheme represents the scheme used to store ethereum states and trie
|
||||||
|
// nodes on top. It can be 'hash', 'path', or none which means use the scheme
|
||||||
|
// consistent with persistent state.
|
||||||
|
StateScheme string `toml:",omitempty"`
|
||||||
|
|
||||||
// RequiredBlocks is a set of block number -> hash mappings which must be in the
|
// RequiredBlocks is a set of block number -> hash mappings which must be in the
|
||||||
// canonical chain of all remote peers. Setting the option makes geth verify the
|
// canonical chain of all remote peers. Setting the option makes geth verify the
|
||||||
|
Loading…
Reference in New Issue
Block a user