diff --git a/cmd/geth/config.go b/cmd/geth/config.go index b1744c8040..8e95941c6f 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -134,6 +134,9 @@ func loadBaseConfig(ctx *cli.Context) gethConfig { utils.Fatalf("%v", err) } } + if !utils.ValidateStateScheme(cfg.Eth.StateScheme) { + utils.Fatalf("invalid state scheme param in config: %s", cfg.Eth.StateScheme) + } // Apply flags. utils.SetNodeConfig(ctx, &cfg.Node) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a0c2e73aca..21ef8398a3 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -51,7 +51,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" @@ -1941,7 +1940,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } // Parse state scheme, abort the process if it's not compatible. chaindb := tryMakeReadOnlyDatabase(ctx, stack) - scheme, err := ParseStateScheme(ctx, chaindb) + scheme, err := ResolveStateScheme(ctx, cfg.StateScheme, chaindb) chaindb.Close() if err != nil { Fatalf("%v", err) @@ -2487,6 +2486,48 @@ func MakeConsolePreloads(ctx *cli.Context) []string { return preloads } +// ResolveStateScheme resolve state scheme from CLI flag, config file and persistent state. +// +// 1. If persistent state and cli is empty, use config param(if user doesn't provide config, use default hash scheme). +// 2. If persistent state is empty, provide CLI flag and config, choose CLI to return. +// 3. If persistent state is nonempty and CLI doesn't be provided, persistent state should be equal to config. +// 4. If all three items are provided: if any two of the three are not equal, return error. +func ResolveStateScheme(ctx *cli.Context, stateSchemeCfg string, disk ethdb.Database) (string, error) { + stored := rawdb.ReadStateScheme(disk) + if stored == "" { + // there is no persistent state data in disk db(e.g. geth init) + if !ctx.IsSet(StateSchemeFlag.Name) { + log.Info("State scheme set by config", "scheme", stateSchemeCfg) + return stateSchemeCfg, nil + } + // if both CLI flag and config are set, choose CLI + scheme := ctx.String(StateSchemeFlag.Name) + if !ValidateStateScheme(scheme) { + return "", fmt.Errorf("invalid state scheme param in CLI: %s", scheme) + } + log.Info("State scheme set by CLI", "scheme", scheme) + return scheme, nil + } + // if there is persistent state data in disk db, and CLI flag, config are set, + // when they all are different, return error + if !ctx.IsSet(StateSchemeFlag.Name) { + if stored != stateSchemeCfg { + return "", fmt.Errorf("incompatible state scheme, stored: %s, config: %s", stored, stateSchemeCfg) + } + log.Info("State scheme set to already existing", "scheme", stored) + return stored, nil + } + scheme := ctx.String(StateSchemeFlag.Name) + if !ValidateStateScheme(scheme) { + return "", fmt.Errorf("invalid state scheme param in CLI: %s", scheme) + } + if scheme != stored || scheme != stateSchemeCfg || stored != stateSchemeCfg { + return "", fmt.Errorf("incompatible state scheme, stored: %s, config: %s, CLI: %s", stored, stateSchemeCfg, scheme) + } + log.Info("All are provided, state scheme set to already existing", "scheme", stored) + return stored, nil +} + // 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. @@ -2506,7 +2547,7 @@ func ParseStateScheme(ctx *cli.Context, disk ethdb.Database) (string, error) { 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") + log.Info("State scheme set to default", "scheme", "hash") return rawdb.HashScheme, nil } log.Info("State scheme set to already existing", "scheme", stored) @@ -2515,6 +2556,9 @@ func ParseStateScheme(ctx *cli.Context, disk ethdb.Database) (string, error) { // If state scheme is specified, ensure it's compatible with // persistent state. scheme := ctx.String(StateSchemeFlag.Name) + if !ValidateStateScheme(scheme) { + return "", fmt.Errorf("invalid state scheme param in CLI: %s", scheme) + } if stored == "" || scheme == stored { log.Info("State scheme set by user", "scheme", scheme) return scheme, nil @@ -2545,3 +2589,12 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read } return trie.NewDatabase(disk, config) } + +// ValidateStateScheme used to check state scheme whether is valid. +// Valid state scheme: hash and path. +func ValidateStateScheme(stateScheme string) bool { + if stateScheme == rawdb.HashScheme || stateScheme == rawdb.PathScheme { + return true + } + return false +} diff --git a/cmd/utils/flags_test.go b/cmd/utils/flags_test.go index adfdd0903e..85456532f8 100644 --- a/cmd/utils/flags_test.go +++ b/cmd/utils/flags_test.go @@ -20,6 +20,8 @@ package utils import ( "reflect" "testing" + + "github.com/ethereum/go-ethereum/core/rawdb" ) func Test_SplitTagsFlag(t *testing.T) { @@ -62,3 +64,34 @@ func Test_SplitTagsFlag(t *testing.T) { }) } } + +func TestValidateStateScheme(t *testing.T) { + tests := []struct { + name string + arg string + wantResult bool + }{ + { + name: "hash scheme", + arg: rawdb.HashScheme, + wantResult: true, + }, + { + name: "path scheme", + arg: rawdb.PathScheme, + wantResult: true, + }, + { + name: "invalid scheme", + arg: "mockScheme", + wantResult: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ValidateStateScheme(tt.arg); got != tt.wantResult { + t.Errorf("ValidateStateScheme() = %v, want %v", got, tt.wantResult) + } + }) + } +}