eth: disable snapshots for partial state nodes

Partial state nodes don't need snapshots since account data is read
directly from the trie (which is small enough for fast lookups) and
BAL-based block processing never uses snapshots.

- Set SnapshotCache to 0 when partial state is enabled (flags.go)
- Allow snap sync without snapshots for partial state mode (handler.go)
- Add nil-check for Snapshots() in snap request handlers to prevent
  panics when serving HashScheme peers (snap/handler.go)
This commit is contained in:
CPerezz 2026-02-08 00:48:49 +01:00
parent 9493cb30fc
commit e48ede038d
No known key found for this signature in database
GPG key ID: 62045F34B97177DD
3 changed files with 26 additions and 6 deletions

View file

@ -1891,6 +1891,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.IsSet(PartialStateChainRetentionFlag.Name) {
cfg.PartialState.ChainRetention = ctx.Uint64(PartialStateChainRetentionFlag.Name)
}
// Partial state nodes don't need snapshots — account data is read
// directly from the trie (which is small enough for fast lookups),
// and BAL-based block processing never uses snapshots.
if cfg.PartialState.Enabled {
cfg.SnapshotCache = 0
}
// Parse transaction history flag, if user is still using legacy config
// file with 'TxLookupLimit' configured, copy the value to 'TransactionHistory'.
if cfg.TransactionHistory == ethconfig.Defaults.TransactionHistory && cfg.TxLookupLimit != ethconfig.Defaults.TxLookupLimit {
@ -1946,8 +1952,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
cfg.RangeLimit = ctx.Uint64(RPCGlobalRangeLimitFlag.Name)
}
if !ctx.Bool(SnapshotFlag.Name) || cfg.SnapshotCache == 0 {
// If snap-sync is requested, this flag is also required
if cfg.SyncMode == ethconfig.SnapSync {
// If snap-sync is requested, this flag is also required (unless
// partial state mode is active, which disables snapshots entirely).
if cfg.SyncMode == ethconfig.SnapSync && !cfg.PartialState.Enabled {
if !ctx.Bool(SnapshotFlag.Name) {
log.Warn("Snap sync requested, enabling --snapshot")
}

View file

@ -168,9 +168,14 @@ func newHandler(config *handlerConfig) (*handler, error) {
// Construct the downloader (long sync)
h.downloader = downloader.New(config.Database, config.Sync, h.eventMux, h.chain, h.removePeer, h.enableSyncedFeatures, config.PartialFilter, config.ChainRetention)
// If snap sync is requested but snapshots are disabled, fail loudly
// If snap sync is requested but snapshots are disabled, fail loudly.
// Partial state nodes are an exception: they disable snapshots intentionally
// (account data is read directly from the trie, BAL processing never uses snapshots).
if h.downloader.ConfigSyncMode() == ethconfig.SnapSync && (config.Chain.Snapshots() == nil && config.Chain.TrieDB().Scheme() == rawdb.HashScheme) {
return nil, errors.New("snap sync not supported with snapshots disabled")
if !config.Chain.SupportsPartialState() {
return nil, errors.New("snap sync not supported with snapshots disabled")
}
log.Info("Snap sync with snapshots disabled (partial state mode)")
}
fetchTx := func(peer string, hashes []common.Hash) error {
p := h.peers.peer(peer)

View file

@ -338,7 +338,11 @@ func ServiceGetAccountRangeQuery(chain *core.BlockChain, req *GetAccountRangePac
var it snapshot.AccountIterator
if chain.TrieDB().Scheme() == rawdb.HashScheme {
// The snapshot is assumed to be available in hash mode if
// the SNAP protocol is enabled.
// the SNAP protocol is enabled. Partial state nodes disable
// snapshots, so bail out gracefully if unavailable.
if chain.Snapshots() == nil {
return nil, nil
}
it, err = chain.Snapshots().AccountIterator(req.Root, req.Origin)
} else {
it, err = chain.TrieDB().AccountIterator(req.Root, req.Origin)
@ -430,7 +434,11 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP
// This can be removed once the hash scheme is deprecated.
if chain.TrieDB().Scheme() == rawdb.HashScheme {
// The snapshot is assumed to be available in hash mode if
// the SNAP protocol is enabled.
// the SNAP protocol is enabled. Partial state nodes disable
// snapshots, so bail out gracefully if unavailable.
if chain.Snapshots() == nil {
return nil, nil
}
it, err = chain.Snapshots().StorageIterator(req.Root, account, origin)
} else {
it, err = chain.TrieDB().StorageIterator(req.Root, account, origin)