Skip to content

Commit eeb5dc3

Browse files
authored
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
1 parent 13d1d42 commit eeb5dc3

File tree

7 files changed

+58
-92
lines changed

7 files changed

+58
-92
lines changed

cmd/geth/chaincmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ func dump(ctx *cli.Context) error {
474474
if err != nil {
475475
return err
476476
}
477-
triedb := utils.MakeTrieDatabase(ctx, db, true, false) // always enable preimage lookup
477+
triedb := utils.MakeTrieDatabase(ctx, db, true, true) // always enable preimage lookup
478478
defer triedb.Close()
479479

480480
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)

cmd/utils/flags.go

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,6 @@ var (
268268
StateSchemeFlag = &cli.StringFlag{
269269
Name: "state.scheme",
270270
Usage: "Scheme to use for storing ethereum state ('hash' or 'path')",
271-
Value: rawdb.HashScheme,
272271
Category: flags.StateCategory,
273272
}
274273
StateHistoryFlag = &cli.Uint64Flag{
@@ -1721,15 +1720,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
17211720
if ctx.IsSet(StateHistoryFlag.Name) {
17221721
cfg.StateHistory = ctx.Uint64(StateHistoryFlag.Name)
17231722
}
1724-
// Parse state scheme, abort the process if it's not compatible.
1725-
chaindb := tryMakeReadOnlyDatabase(ctx, stack)
1726-
scheme, err := ParseStateScheme(ctx, chaindb)
1727-
chaindb.Close()
1728-
if err != nil {
1729-
Fatalf("%v", err)
1723+
if ctx.IsSet(StateSchemeFlag.Name) {
1724+
cfg.StateScheme = ctx.String(StateSchemeFlag.Name)
17301725
}
1731-
cfg.StateScheme = scheme
1732-
17331726
// Parse transaction history flag, if user is still using legacy config
17341727
// file with 'TxLookupLimit' configured, copy the value to 'TransactionHistory'.
17351728
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
21652158
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
21662159
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
21672160
}
2168-
scheme, err := ParseStateScheme(ctx, chainDb)
2161+
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), chainDb)
21692162
if err != nil {
21702163
Fatalf("%v", err)
21712164
}
@@ -2224,47 +2217,12 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
22242217
return preloads
22252218
}
22262219

2227-
// ParseStateScheme resolves scheme identifier from CLI flag. If the provided
2228-
// state scheme is not compatible with the one of persistent scheme, an error
2229-
// will be returned.
2230-
//
2231-
// - none: use the scheme consistent with persistent state, or fallback
2232-
// to hash-based scheme if state is empty.
2233-
// - hash: use hash-based scheme or error out if not compatible with
2234-
// persistent state scheme.
2235-
// - path: use path-based scheme or error out if not compatible with
2236-
// persistent state scheme.
2237-
func ParseStateScheme(ctx *cli.Context, disk ethdb.Database) (string, error) {
2238-
// If state scheme is not specified, use the scheme consistent
2239-
// with persistent state, or fallback to hash mode if database
2240-
// is empty.
2241-
stored := rawdb.ReadStateScheme(disk)
2242-
if !ctx.IsSet(StateSchemeFlag.Name) {
2243-
if stored == "" {
2244-
// use default scheme for empty database, flip it when
2245-
// path mode is chosen as default
2246-
log.Info("State schema set to default", "scheme", "hash")
2247-
return rawdb.HashScheme, nil
2248-
}
2249-
log.Info("State scheme set to already existing", "scheme", stored)
2250-
return stored, nil // reuse scheme of persistent scheme
2251-
}
2252-
// If state scheme is specified, ensure it's compatible with
2253-
// persistent state.
2254-
scheme := ctx.String(StateSchemeFlag.Name)
2255-
if stored == "" || scheme == stored {
2256-
log.Info("State scheme set by user", "scheme", scheme)
2257-
return scheme, nil
2258-
}
2259-
return "", fmt.Errorf("incompatible state scheme, stored: %s, provided: %s", stored, scheme)
2260-
}
2261-
22622220
// MakeTrieDatabase constructs a trie database based on the configured scheme.
22632221
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool) *trie.Database {
22642222
config := &trie.Config{
22652223
Preimages: preimage,
22662224
}
2267-
scheme, err := ParseStateScheme(ctx, disk)
2225+
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
22682226
if err != nil {
22692227
Fatalf("%v", err)
22702228
}

core/genesis.go

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
120120
return nil
121121
}
122122

123-
// deriveHash computes the state root according to the genesis specification.
124-
func (ga *GenesisAlloc) deriveHash() (common.Hash, error) {
123+
// hash computes the state root according to the genesis specification.
124+
func (ga *GenesisAlloc) hash() (common.Hash, error) {
125125
// Create an ephemeral in-memory database for computing hash,
126126
// all the derived states will be discarded to not pollute disk.
127127
db := state.NewDatabase(rawdb.NewMemoryDatabase())
@@ -142,9 +142,9 @@ func (ga *GenesisAlloc) deriveHash() (common.Hash, error) {
142142
return statedb.Commit(0, false)
143143
}
144144

145-
// flush is very similar with deriveHash, but the main difference is
146-
// all the generated states will be persisted into the given database.
147-
// Also, the genesis state specification will be flushed as well.
145+
// flush is very similar with hash, but the main difference is all the generated
146+
// states will be persisted into the given database. Also, the genesis state
147+
// specification will be flushed as well.
148148
func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
149149
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
150150
if err != nil {
@@ -179,39 +179,6 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
179179
return nil
180180
}
181181

182-
// CommitGenesisState loads the stored genesis state with the given block
183-
// hash and commits it into the provided trie database.
184-
func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
185-
var alloc GenesisAlloc
186-
blob := rawdb.ReadGenesisStateSpec(db, blockhash)
187-
if len(blob) != 0 {
188-
if err := alloc.UnmarshalJSON(blob); err != nil {
189-
return err
190-
}
191-
} else {
192-
// Genesis allocation is missing and there are several possibilities:
193-
// the node is legacy which doesn't persist the genesis allocation or
194-
// the persisted allocation is just lost.
195-
// - supported networks(mainnet, testnets), recover with defined allocations
196-
// - private network, can't recover
197-
var genesis *Genesis
198-
switch blockhash {
199-
case params.MainnetGenesisHash:
200-
genesis = DefaultGenesisBlock()
201-
case params.GoerliGenesisHash:
202-
genesis = DefaultGoerliGenesisBlock()
203-
case params.SepoliaGenesisHash:
204-
genesis = DefaultSepoliaGenesisBlock()
205-
}
206-
if genesis != nil {
207-
alloc = genesis.Alloc
208-
} else {
209-
return errors.New("not found")
210-
}
211-
}
212-
return alloc.flush(db, triedb, blockhash)
213-
}
214-
215182
// GenesisAccount is an account in the state of the genesis block.
216183
type GenesisAccount struct {
217184
Code []byte `json:"code,omitempty"`
@@ -444,7 +411,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
444411

445412
// ToBlock returns the genesis block according to genesis specification.
446413
func (g *Genesis) ToBlock() *types.Block {
447-
root, err := g.Alloc.deriveHash()
414+
root, err := g.Alloc.hash()
448415
if err != nil {
449416
panic(err)
450417
}

core/genesis_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
231231
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
232232
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
233233
}
234-
hash, _ = alloc.deriveHash()
234+
hash, _ = alloc.hash()
235235
)
236236
blob, _ := json.Marshal(alloc)
237237
rawdb.WriteGenesisStateSpec(db, hash, blob)

core/rawdb/accessors_trie.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,38 @@ func ReadStateScheme(db ethdb.Reader) string {
305305
}
306306
return HashScheme
307307
}
308+
309+
// ParseStateScheme checks if the specified state scheme is compatible with
310+
// the stored state.
311+
//
312+
// - If the provided scheme is none, use the scheme consistent with persistent
313+
// state, or fallback to hash-based scheme if state is empty.
314+
//
315+
// - If the provided scheme is hash, use hash-based scheme or error out if not
316+
// compatible with persistent state scheme.
317+
//
318+
// - If the provided scheme is path: use path-based scheme or error out if not
319+
// compatible with persistent state scheme.
320+
func ParseStateScheme(provided string, disk ethdb.Database) (string, error) {
321+
// If state scheme is not specified, use the scheme consistent
322+
// with persistent state, or fallback to hash mode if database
323+
// is empty.
324+
stored := ReadStateScheme(disk)
325+
if provided == "" {
326+
if stored == "" {
327+
// use default scheme for empty database, flip it when
328+
// path mode is chosen as default
329+
log.Info("State schema set to default", "scheme", "hash")
330+
return HashScheme, nil
331+
}
332+
log.Info("State scheme set to already existing", "scheme", stored)
333+
return stored, nil // reuse scheme of persistent scheme
334+
}
335+
// If state scheme is specified, ensure it's compatible with
336+
// persistent state.
337+
if stored == "" || provided == stored {
338+
log.Info("State scheme set by user", "scheme", provided)
339+
return provided, nil
340+
}
341+
return "", fmt.Errorf("incompatible state scheme, stored: %s, provided: %s", stored, provided)
342+
}

eth/backend.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,12 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
133133
if err != nil {
134134
return nil, err
135135
}
136+
scheme, err := rawdb.ParseStateScheme(config.StateScheme, chainDb)
137+
if err != nil {
138+
return nil, err
139+
}
136140
// Try to recover offline state pruning only in hash-based.
137-
if config.StateScheme == rawdb.HashScheme {
141+
if scheme == rawdb.HashScheme {
138142
if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb); err != nil {
139143
log.Error("Failed to recover state", "error", err)
140144
}
@@ -194,7 +198,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
194198
SnapshotLimit: config.SnapshotCache,
195199
Preimages: config.Preimages,
196200
StateHistory: config.StateHistory,
197-
StateScheme: config.StateScheme,
201+
StateScheme: scheme,
198202
}
199203
)
200204
// Override the chain config with provided settings.

eth/ethconfig/config.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"github.com/ethereum/go-ethereum/consensus/clique"
2828
"github.com/ethereum/go-ethereum/consensus/ethash"
2929
"github.com/ethereum/go-ethereum/core"
30-
"github.com/ethereum/go-ethereum/core/rawdb"
3130
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
3231
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
3332
"github.com/ethereum/go-ethereum/eth/downloader"
@@ -64,7 +63,6 @@ var Defaults = Config{
6463
TxLookupLimit: 2350000,
6564
TransactionHistory: 2350000,
6665
StateHistory: params.FullImmutabilityThreshold,
67-
StateScheme: rawdb.HashScheme,
6866
LightPeers: 100,
6967
DatabaseCache: 512,
7068
TrieCleanCache: 154,
@@ -105,7 +103,11 @@ type Config struct {
105103
TxLookupLimit uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
106104
TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
107105
StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved.
108-
StateScheme string `toml:",omitempty"` // State scheme used to store ethereum state and merkle trie nodes on top
106+
107+
// State scheme represents the scheme used to store ethereum states and trie
108+
// nodes on top. It can be 'hash', 'path', or none which means use the scheme
109+
// consistent with persistent state.
110+
StateScheme string `toml:",omitempty"`
109111

110112
// RequiredBlocks is a set of block number -> hash mappings which must be in the
111113
// canonical chain of all remote peers. Setting the option makes geth verify the

0 commit comments

Comments
 (0)