From 6cc1efa4b52cdf2431cb49206623a0d9668be632 Mon Sep 17 00:00:00 2001 From: flywukong <19421226+flywukong@users.noreply.github.com> Date: Tue, 13 Jan 2026 15:29:36 +0800 Subject: [PATCH 1/5] feat: support import/export checkpoint file for filtermap --- cmd/geth/chaincmd.go | 1 - cmd/geth/main.go | 1 - cmd/utils/flags.go | 9 ---- core/filtermaps/filtermaps.go | 78 +++++++++++++++++++++++++------ core/filtermaps/indexer.go | 2 +- eth/backend.go | 16 +++++-- eth/ethconfig/config.go | 1 - eth/ethconfig/gen_config.go | 26 ++++------- eth/filters/filter_system_test.go | 6 +-- 9 files changed, 89 insertions(+), 51 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 2a573e1056..efdcfcd81c 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -159,7 +159,6 @@ if one is set. Otherwise it prints the genesis from the datadir.`, utils.TransactionHistoryFlag, utils.LogHistoryFlag, utils.LogNoHistoryFlag, - utils.LogExportCheckpointsFlag, utils.StateHistoryFlag, }, utils.DatabaseFlags, debug.Flags), Before: func(ctx *cli.Context) error { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index eb2c24cf9a..7c1727f725 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -117,7 +117,6 @@ var ( // utils.ChainHistoryFlag, // disabled in bsc utils.LogHistoryFlag, utils.LogNoHistoryFlag, - utils.LogExportCheckpointsFlag, utils.StateHistoryFlag, utils.PathDBSyncFlag, utils.JournalFileFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 852280ab10..74f3611266 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -438,12 +438,6 @@ var ( Usage: "Do not maintain log search index", Category: flags.StateCategory, } - LogExportCheckpointsFlag = &cli.StringFlag{ - Name: "history.logs.export", - Usage: "Export checkpoints to file in go source file format", - Category: flags.StateCategory, - Value: "", - } // Beacon client light sync settings BeaconApiFlag = &cli.StringSliceFlag{ Name: "beacon.api", @@ -2217,9 +2211,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(LogNoHistoryFlag.Name) { cfg.LogNoHistory = true } - if ctx.IsSet(LogExportCheckpointsFlag.Name) { - cfg.LogExportCheckpoints = ctx.String(LogExportCheckpointsFlag.Name) - } if ctx.String(GCModeFlag.Name) == "archive" && cfg.BlockHistory != 0 { cfg.BlockHistory = 0 log.Warn("Disabled partial block reserve for archive node") diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index 739e1f86bf..6fc0f6d1fb 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -17,6 +17,7 @@ package filtermaps import ( + "encoding/json" "errors" "fmt" "os" @@ -76,7 +77,7 @@ type FilterMaps struct { closeWg sync.WaitGroup history uint64 hashScheme bool // use hashdb-safe delete range method - exportFileName string + checkpointFile string Params db ethdb.KeyValueStore @@ -216,9 +217,10 @@ type Config struct { History uint64 // number of historical blocks to index Disabled bool // disables indexing completely - // This option enables the checkpoint JSON file generator. - // If set, the given file will be updated with checkpoint information. - ExportFileName string + // CheckpointFileName specifies the path to the checkpoint JSON file. + // If set, checkpoints will be loaded from this file during initialization (import), + // and the file will be updated with new checkpoint information during operation (export). + CheckpointFileName string // expect trie nodes of hash based state scheme in the filtermaps key range; // use safe iterator based implementation of DeleteRange that skips them @@ -245,7 +247,7 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f disabled: config.Disabled, hashScheme: config.HashScheme, disabledCh: make(chan struct{}), - exportFileName: config.ExportFileName, + checkpointFile: config.CheckpointFileName, Params: params, targetView: initView, indexedView: initView, @@ -370,6 +372,17 @@ func (f *FilterMaps) isShuttingDown() bool { } } +// loadCustomCheckpoints safely loads and parses a checkpoint list from JSON data. +// Returns an empty list if the data is invalid or parsing fails. +func loadCustomCheckpoints(data []byte) checkpointList { + var result checkpointList + if err := json.Unmarshal(data, &result); err != nil { + log.Warn("Failed to parse custom checkpoint file", "error", err) + return nil + } + return result +} + // init initializes an empty log index according to the current targetView. func (f *FilterMaps) init() error { // ensure that there is no remaining data in the filter maps key range @@ -380,26 +393,58 @@ func (f *FilterMaps) init() error { f.indexLock.Lock() defer f.indexLock.Unlock() + // Load checkpoints from custom file if specified + allCheckpoints := checkpoints + if f.checkpointFile != "" { + if data, err := os.ReadFile(f.checkpointFile); err == nil { + if customCheckpoints := loadCustomCheckpoints(data); len(customCheckpoints) > 0 { + // Create a new slice with embedded checkpoints + custom checkpoints + allCheckpoints = make([]checkpointList, len(checkpoints)+1) + copy(allCheckpoints, checkpoints) + allCheckpoints[len(checkpoints)] = customCheckpoints + log.Info("Loaded custom checkpoints from file", + "path", f.checkpointFile, + "count", len(customCheckpoints)) + } + } else { + log.Debug("Could not load custom checkpoint file", + "path", f.checkpointFile, + "error", err) + } + } + var bestIdx, bestLen int - for idx, checkpointList := range checkpoints { - // binary search for the last matching epoch head + for idx, checkpointList := range allCheckpoints { + // binary search to find the last checkpoint that is <= headNumber min, max := 0, len(checkpointList) for min < max { mid := (min + max + 1) / 2 - cp := checkpointList[mid-1] - if cp.BlockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(cp.BlockNumber) == cp.BlockId { + if checkpointList[mid-1].BlockNumber <= f.targetView.HeadNumber() { min = mid } else { max = mid - 1 } } + if max == 0 { + continue + } + // verify the latest checkpoint within range + cp := checkpointList[max-1] + if f.targetView.BlockId(cp.BlockNumber) != cp.BlockId { + continue + } if max > bestLen { bestIdx, bestLen = idx, max } } var initBlockNumber uint64 if bestLen > 0 { - initBlockNumber = checkpoints[bestIdx][bestLen-1].BlockNumber + initBlockNumber = allCheckpoints[bestIdx][bestLen-1].BlockNumber + log.Info("Using checkpoint for initialization", + "checkpointBlock", initBlockNumber, + "matchedCheckpoints", bestLen) + } else { + log.Warn("No matching checkpoints found, starting from genesis") } if initBlockNumber < f.historyCutoff { return errors.New("cannot start indexing before history cutoff point") @@ -410,12 +455,15 @@ func (f *FilterMaps) init() error { initBlockNumber = 1 } if f.indexedView.chain.GetCanonicalHash(initBlockNumber) == (common.Hash{}) { + log.Error("Init block is pruned", + "initBlock", initBlockNumber, + "headNumber", f.targetView.HeadNumber()) return fmt.Errorf("cannot start indexing: blockNumber=%d is pruned", initBlockNumber) } } batch := f.db.NewBatch() for epoch := range bestLen { - cp := checkpoints[bestIdx][epoch] + cp := allCheckpoints[bestIdx][epoch] f.storeLastBlockOfMap(batch, f.lastEpochMap(uint32(epoch)), cp.BlockNumber, cp.BlockId) f.storeBlockLvPointer(batch, cp.BlockNumber, cp.FirstIndex) } @@ -423,7 +471,7 @@ func (f *FilterMaps) init() error { initialized: true, } if bestLen > 0 { - cp := checkpoints[bestIdx][bestLen-1] + cp := allCheckpoints[bestIdx][bestLen-1] fmr.blocks = common.NewRange(cp.BlockNumber+1, 0) fmr.maps = common.NewRange(f.firstEpochMap(uint32(bestLen)), 0) } @@ -871,14 +919,14 @@ func (f *FilterMaps) exportCheckpoints() { if epochCount == f.lastFinalEpoch { return } - w, err := os.Create(f.exportFileName) + w, err := os.Create(f.checkpointFile) if err != nil { - log.Error("Error creating checkpoint export file", "name", f.exportFileName, "error", err) + log.Error("Error creating checkpoint export file", "name", f.checkpointFile, "error", err) return } defer w.Close() - log.Info("Exporting log index checkpoints", "epochs", epochCount, "file", f.exportFileName) + log.Info("Exporting log index checkpoints", "epochs", epochCount, "file", f.checkpointFile) w.WriteString("[\n") comma := "," for epoch := uint32(0); epoch < epochCount; epoch++ { diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index ca50fb466c..ff1cac21b4 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -64,7 +64,7 @@ func (f *FilterMaps) indexerLoop() { } } else { if f.finalBlock != f.lastFinal { - if f.exportFileName != "" { + if f.checkpointFile != "" { f.exportCheckpoints() } f.lastFinal = f.finalBlock diff --git a/eth/backend.go b/eth/backend.go index 754608d77d..ff89b696cf 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -24,6 +24,7 @@ import ( "fmt" "math" "math/big" + "path/filepath" "runtime" "sync" "time" @@ -404,11 +405,18 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } // Initialize filtermaps log index. + // Auto-enable checkpoint file for BSC chains + var checkpointFile string + chainID := eth.blockchain.Config().ChainID.Uint64() + if chainID == 56 || chainID == 97 { // BSC Mainnet or Chapel + checkpointFile = filepath.Join(stack.DataDir(), "geth", "filtermap_checkpoints.json") + } + fmConfig := filtermaps.Config{ - History: config.LogHistory, - Disabled: config.LogNoHistory, - ExportFileName: config.LogExportCheckpoints, - HashScheme: config.StateScheme == rawdb.HashScheme, + History: config.LogHistory, + Disabled: config.LogNoHistory, + CheckpointFileName: checkpointFile, + HashScheme: config.StateScheme == rawdb.HashScheme, } chainView := eth.newChainView(eth.blockchain.CurrentBlock()) historyCutoff, _ := eth.blockchain.HistoryPruningCutoff() diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 499511311f..a602d54c9f 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -126,7 +126,6 @@ type Config struct { BlockHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose block body/header/receipt/diff/hash are reserved. LogHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head where a log search index is maintained. LogNoHistory bool `toml:",omitempty"` // No log search index is maintained. - LogExportCheckpoints string // export log index checkpoints to file StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved. // State scheme represents the scheme used to store ethereum states and trie diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 57d0457aa7..6b8c743e7a 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -34,12 +34,11 @@ func (c Config) MarshalTOML() (interface{}, error) { DirectBroadcast bool DisableSnapProtocol bool RangeLimit bool - TxLookupLimit uint64 `toml:",omitempty"` - TransactionHistory uint64 `toml:",omitempty"` - BlockHistory uint64 `toml:",omitempty"` - LogHistory uint64 `toml:",omitempty"` - LogNoHistory bool `toml:",omitempty"` - LogExportCheckpoints string + TxLookupLimit uint64 `toml:",omitempty"` + TransactionHistory uint64 `toml:",omitempty"` + BlockHistory uint64 `toml:",omitempty"` + LogHistory uint64 `toml:",omitempty"` + LogNoHistory bool `toml:",omitempty"` StateHistory uint64 `toml:",omitempty"` StateScheme string `toml:",omitempty"` PathSyncFlush bool `toml:",omitempty"` @@ -116,7 +115,6 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.BlockHistory = c.BlockHistory enc.LogHistory = c.LogHistory enc.LogNoHistory = c.LogNoHistory - enc.LogExportCheckpoints = c.LogExportCheckpoints enc.StateHistory = c.StateHistory enc.StateScheme = c.StateScheme enc.PathSyncFlush = c.PathSyncFlush @@ -192,12 +190,11 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { DirectBroadcast *bool DisableSnapProtocol *bool RangeLimit *bool - TxLookupLimit *uint64 `toml:",omitempty"` - TransactionHistory *uint64 `toml:",omitempty"` - BlockHistory *uint64 `toml:",omitempty"` - LogHistory *uint64 `toml:",omitempty"` - LogNoHistory *bool `toml:",omitempty"` - LogExportCheckpoints *string + TxLookupLimit *uint64 `toml:",omitempty"` + TransactionHistory *uint64 `toml:",omitempty"` + BlockHistory *uint64 `toml:",omitempty"` + LogHistory *uint64 `toml:",omitempty"` + LogNoHistory *bool `toml:",omitempty"` StateHistory *uint64 `toml:",omitempty"` StateScheme *string `toml:",omitempty"` PathSyncFlush *bool `toml:",omitempty"` @@ -319,9 +316,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.LogNoHistory != nil { c.LogNoHistory = *dec.LogNoHistory } - if dec.LogExportCheckpoints != nil { - c.LogExportCheckpoints = *dec.LogExportCheckpoints - } if dec.StateHistory != nil { c.StateHistory = *dec.StateHistory } diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 97b7f7d9be..c5860cec88 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -183,9 +183,9 @@ func (b *testBackend) startFilterMaps(history uint64, disabled bool, params filt head := b.CurrentBlock() chainView := filtermaps.NewChainView(b, head.Number.Uint64(), head.Hash()) config := filtermaps.Config{ - History: history, - Disabled: disabled, - ExportFileName: "", + History: history, + Disabled: disabled, + CheckpointFileName: "", } b.fm, _ = filtermaps.NewFilterMaps(b.db, chainView, 0, 0, params, config) b.fm.Start() From db9befa3473c3dd6078d83e74bae318caae4b70c Mon Sep 17 00:00:00 2001 From: flywukong <19421226+flywukong@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:49:51 +0800 Subject: [PATCH 2/5] fix: fix comment --- cmd/geth/chaincmd.go | 1 + cmd/geth/main.go | 1 + cmd/utils/flags.go | 10 ++++++++++ core/filtermaps/filtermaps.go | 26 +++++--------------------- eth/backend.go | 8 ++------ eth/ethconfig/config.go | 11 +++++++---- eth/ethconfig/gen_config.go | 26 ++++++++++++++++---------- 7 files changed, 42 insertions(+), 41 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index efdcfcd81c..2a573e1056 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -159,6 +159,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`, utils.TransactionHistoryFlag, utils.LogHistoryFlag, utils.LogNoHistoryFlag, + utils.LogExportCheckpointsFlag, utils.StateHistoryFlag, }, utils.DatabaseFlags, debug.Flags), Before: func(ctx *cli.Context) error { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 7c1727f725..eb2c24cf9a 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -117,6 +117,7 @@ var ( // utils.ChainHistoryFlag, // disabled in bsc utils.LogHistoryFlag, utils.LogNoHistoryFlag, + utils.LogExportCheckpointsFlag, utils.StateHistoryFlag, utils.PathDBSyncFlag, utils.JournalFileFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 74f3611266..09f8a9c42b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -438,6 +438,12 @@ var ( Usage: "Do not maintain log search index", Category: flags.StateCategory, } + // Deprecated Jan 2025 + LogExportCheckpointsFlag = &cli.StringFlag{ + Name: "history.logs.export", + Usage: "Deprecated, checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json", + Category: flags.StateCategory, + } // Beacon client light sync settings BeaconApiFlag = &cli.StringSliceFlag{ Name: "beacon.api", @@ -2211,6 +2217,10 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(LogNoHistoryFlag.Name) { cfg.LogNoHistory = true } + if ctx.IsSet(LogExportCheckpointsFlag.Name) { + cfg.LogExportCheckpoints = ctx.String(LogExportCheckpointsFlag.Name) + log.Warn("Flag --history.logs.export is deprecated, checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json") + } if ctx.String(GCModeFlag.Name) == "archive" && cfg.BlockHistory != 0 { cfg.BlockHistory = 0 log.Warn("Disabled partial block reserve for archive node") diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index 6fc0f6d1fb..d6ccdb4013 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -394,27 +394,19 @@ func (f *FilterMaps) init() error { defer f.indexLock.Unlock() // Load checkpoints from custom file if specified - allCheckpoints := checkpoints if f.checkpointFile != "" { if data, err := os.ReadFile(f.checkpointFile); err == nil { if customCheckpoints := loadCustomCheckpoints(data); len(customCheckpoints) > 0 { - // Create a new slice with embedded checkpoints + custom checkpoints - allCheckpoints = make([]checkpointList, len(checkpoints)+1) - copy(allCheckpoints, checkpoints) - allCheckpoints[len(checkpoints)] = customCheckpoints + checkpoints = append(checkpoints, customCheckpoints) log.Info("Loaded custom checkpoints from file", "path", f.checkpointFile, "count", len(customCheckpoints)) } - } else { - log.Debug("Could not load custom checkpoint file", - "path", f.checkpointFile, - "error", err) } } var bestIdx, bestLen int - for idx, checkpointList := range allCheckpoints { + for idx, checkpointList := range checkpoints { // binary search to find the last checkpoint that is <= headNumber min, max := 0, len(checkpointList) for min < max { @@ -439,12 +431,7 @@ func (f *FilterMaps) init() error { } var initBlockNumber uint64 if bestLen > 0 { - initBlockNumber = allCheckpoints[bestIdx][bestLen-1].BlockNumber - log.Info("Using checkpoint for initialization", - "checkpointBlock", initBlockNumber, - "matchedCheckpoints", bestLen) - } else { - log.Warn("No matching checkpoints found, starting from genesis") + initBlockNumber = checkpoints[bestIdx][bestLen-1].BlockNumber } if initBlockNumber < f.historyCutoff { return errors.New("cannot start indexing before history cutoff point") @@ -455,15 +442,12 @@ func (f *FilterMaps) init() error { initBlockNumber = 1 } if f.indexedView.chain.GetCanonicalHash(initBlockNumber) == (common.Hash{}) { - log.Error("Init block is pruned", - "initBlock", initBlockNumber, - "headNumber", f.targetView.HeadNumber()) return fmt.Errorf("cannot start indexing: blockNumber=%d is pruned", initBlockNumber) } } batch := f.db.NewBatch() for epoch := range bestLen { - cp := allCheckpoints[bestIdx][epoch] + cp := checkpoints[bestIdx][epoch] f.storeLastBlockOfMap(batch, f.lastEpochMap(uint32(epoch)), cp.BlockNumber, cp.BlockId) f.storeBlockLvPointer(batch, cp.BlockNumber, cp.FirstIndex) } @@ -471,7 +455,7 @@ func (f *FilterMaps) init() error { initialized: true, } if bestLen > 0 { - cp := allCheckpoints[bestIdx][bestLen-1] + cp := checkpoints[bestIdx][bestLen-1] fmr.blocks = common.NewRange(cp.BlockNumber+1, 0) fmr.maps = common.NewRange(f.firstEpochMap(uint32(bestLen)), 0) } diff --git a/eth/backend.go b/eth/backend.go index ff89b696cf..5679eaedb3 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -405,12 +405,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } // Initialize filtermaps log index. - // Auto-enable checkpoint file for BSC chains - var checkpointFile string - chainID := eth.blockchain.Config().ChainID.Uint64() - if chainID == 56 || chainID == 97 { // BSC Mainnet or Chapel - checkpointFile = filepath.Join(stack.DataDir(), "geth", "filtermap_checkpoints.json") - } + // Auto-enable checkpoint file + checkpointFile := filepath.Join(stack.DataDir(), "geth", "filtermap_checkpoints.json") fmConfig := filtermaps.Config{ History: config.LogHistory, diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index a602d54c9f..657ca8816d 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -122,10 +122,13 @@ type Config struct { // Deprecated: use 'TransactionHistory' instead. 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. - BlockHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose block body/header/receipt/diff/hash are reserved. - LogHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head where a log search index is maintained. - LogNoHistory bool `toml:",omitempty"` // No log search index is maintained. + TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved. + BlockHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose block body/header/receipt/diff/hash are reserved. + LogHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head where a log search index is maintained. + LogNoHistory bool `toml:",omitempty"` // No log search index is maintained. + // LogExportCheckpoints exports log index checkpoints to file. + // Deprecated: checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json. + LogExportCheckpoints string StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved. // State scheme represents the scheme used to store ethereum states and trie diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 6b8c743e7a..57d0457aa7 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -34,11 +34,12 @@ func (c Config) MarshalTOML() (interface{}, error) { DirectBroadcast bool DisableSnapProtocol bool RangeLimit bool - TxLookupLimit uint64 `toml:",omitempty"` - TransactionHistory uint64 `toml:",omitempty"` - BlockHistory uint64 `toml:",omitempty"` - LogHistory uint64 `toml:",omitempty"` - LogNoHistory bool `toml:",omitempty"` + TxLookupLimit uint64 `toml:",omitempty"` + TransactionHistory uint64 `toml:",omitempty"` + BlockHistory uint64 `toml:",omitempty"` + LogHistory uint64 `toml:",omitempty"` + LogNoHistory bool `toml:",omitempty"` + LogExportCheckpoints string StateHistory uint64 `toml:",omitempty"` StateScheme string `toml:",omitempty"` PathSyncFlush bool `toml:",omitempty"` @@ -115,6 +116,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.BlockHistory = c.BlockHistory enc.LogHistory = c.LogHistory enc.LogNoHistory = c.LogNoHistory + enc.LogExportCheckpoints = c.LogExportCheckpoints enc.StateHistory = c.StateHistory enc.StateScheme = c.StateScheme enc.PathSyncFlush = c.PathSyncFlush @@ -190,11 +192,12 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { DirectBroadcast *bool DisableSnapProtocol *bool RangeLimit *bool - TxLookupLimit *uint64 `toml:",omitempty"` - TransactionHistory *uint64 `toml:",omitempty"` - BlockHistory *uint64 `toml:",omitempty"` - LogHistory *uint64 `toml:",omitempty"` - LogNoHistory *bool `toml:",omitempty"` + TxLookupLimit *uint64 `toml:",omitempty"` + TransactionHistory *uint64 `toml:",omitempty"` + BlockHistory *uint64 `toml:",omitempty"` + LogHistory *uint64 `toml:",omitempty"` + LogNoHistory *bool `toml:",omitempty"` + LogExportCheckpoints *string StateHistory *uint64 `toml:",omitempty"` StateScheme *string `toml:",omitempty"` PathSyncFlush *bool `toml:",omitempty"` @@ -316,6 +319,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.LogNoHistory != nil { c.LogNoHistory = *dec.LogNoHistory } + if dec.LogExportCheckpoints != nil { + c.LogExportCheckpoints = *dec.LogExportCheckpoints + } if dec.StateHistory != nil { c.StateHistory = *dec.StateHistory } From 3e64d9b0d8fcad3b8dc1409aa3804e7632662a9e Mon Sep 17 00:00:00 2001 From: flywukong <19421226+flywukong@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:30:26 +0800 Subject: [PATCH 3/5] fix: improve code --- cmd/utils/flags.go | 3 +-- core/filtermaps/filtermaps.go | 6 +++--- eth/ethconfig/config.go | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 09f8a9c42b..252d0f1a72 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -441,7 +441,7 @@ var ( // Deprecated Jan 2025 LogExportCheckpointsFlag = &cli.StringFlag{ Name: "history.logs.export", - Usage: "Deprecated, checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json", + Usage: "Export checkpoints to file in go source file format", Category: flags.StateCategory, } // Beacon client light sync settings @@ -2219,7 +2219,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } if ctx.IsSet(LogExportCheckpointsFlag.Name) { cfg.LogExportCheckpoints = ctx.String(LogExportCheckpointsFlag.Name) - log.Warn("Flag --history.logs.export is deprecated, checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json") } if ctx.String(GCModeFlag.Name) == "archive" && cfg.BlockHistory != 0 { cfg.BlockHistory = 0 diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index d6ccdb4013..215c48cc9d 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -218,8 +218,8 @@ type Config struct { Disabled bool // disables indexing completely // CheckpointFileName specifies the path to the checkpoint JSON file. - // If set, checkpoints will be loaded from this file during initialization (import), - // and the file will be updated with new checkpoint information during operation (export). + // If set, checkpoints will be loaded from this file during initialization, + // and the file will be updated with new checkpoint information during operation. CheckpointFileName string // expect trie nodes of hash based state scheme in the filtermaps key range; @@ -407,7 +407,7 @@ func (f *FilterMaps) init() error { var bestIdx, bestLen int for idx, checkpointList := range checkpoints { - // binary search to find the last checkpoint that is <= headNumber + // binary search for the last matching epoch head min, max := 0, len(checkpointList) for min < max { mid := (min + max + 1) / 2 diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 657ca8816d..68c1a8ee75 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -126,8 +126,7 @@ type Config struct { BlockHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose block body/header/receipt/diff/hash are reserved. LogHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head where a log search index is maintained. LogNoHistory bool `toml:",omitempty"` // No log search index is maintained. - // LogExportCheckpoints exports log index checkpoints to file. - // Deprecated: checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json. + // Deprecated: exporting checkpoint file is auto-enabled. LogExportCheckpoints string StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved. From b5c3b2efd3409ddd86418c509fd13c0b0b38a78f Mon Sep 17 00:00:00 2001 From: flywukong <19421226+flywukong@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:17:36 +0800 Subject: [PATCH 4/5] fix: improve deprecated flag comments --- cmd/utils/flags.go | 5 +++-- cmd/utils/flags_legacy.go | 1 + eth/ethconfig/config.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 252d0f1a72..27ddbb69ce 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -441,8 +441,9 @@ var ( // Deprecated Jan 2025 LogExportCheckpointsFlag = &cli.StringFlag{ Name: "history.logs.export", - Usage: "Export checkpoints to file in go source file format", - Category: flags.StateCategory, + Usage: "Deprecated, checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json", + Category: flags.DeprecatedCategory, + Value: "", } // Beacon client light sync settings BeaconApiFlag = &cli.StringSliceFlag{ diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go index a3a50e95b3..1ac54c5742 100644 --- a/cmd/utils/flags_legacy.go +++ b/cmd/utils/flags_legacy.go @@ -46,6 +46,7 @@ var DeprecatedFlags = []cli.Flag{ EnablePersonal, PruneAncientDataFlag, JournalFileFlag, + LogExportCheckpointsFlag, } var ( diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 68c1a8ee75..06599b80ce 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -126,7 +126,7 @@ type Config struct { BlockHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose block body/header/receipt/diff/hash are reserved. LogHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head where a log search index is maintained. LogNoHistory bool `toml:",omitempty"` // No log search index is maintained. - // Deprecated: exporting checkpoint file is auto-enabled. + // Deprecated: checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json. LogExportCheckpoints string StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved. From 6f127402cf86e266d4a6e77b4c896c3e9f732391 Mon Sep 17 00:00:00 2001 From: flywukong <19421226+flywukong@users.noreply.github.com> Date: Mon, 19 Jan 2026 13:13:48 +0800 Subject: [PATCH 5/5] fix: add warn log --- cmd/utils/flags.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 27ddbb69ce..df8c7778da 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2220,6 +2220,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } if ctx.IsSet(LogExportCheckpointsFlag.Name) { cfg.LogExportCheckpoints = ctx.String(LogExportCheckpointsFlag.Name) + log.Warn("Flag --history.logs.export is deprecated, checkpoint file is auto-enabled at datadir/geth/filtermap_checkpoints.json") } if ctx.String(GCModeFlag.Name) == "archive" && cfg.BlockHistory != 0 { cfg.BlockHistory = 0