core/filtermaps: safely revert reorged log index during init

This commit is contained in:
Zsolt Felfoldi 2025-05-01 05:42:39 +02:00
parent 6ed74f0e60
commit 18b3664bd1
3 changed files with 37 additions and 40 deletions

View file

@ -135,14 +135,6 @@ func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
return common.NewRange(0, sharedLen) return common.NewRange(0, sharedLen)
} }
// limitedView returns a new chain view that is a truncated version of the parent view.
func (cv *ChainView) limitedView(newHead uint64) *ChainView {
if newHead >= cv.headNumber {
return cv
}
return NewChainView(cv.chain, newHead, cv.BlockHash(newHead))
}
// equalViews returns true if the two chain views are equivalent. // equalViews returns true if the two chain views are equivalent.
func equalViews(cv1, cv2 *ChainView) bool { func equalViews(cv1, cv2 *ChainView) bool {
if cv1 == nil || cv2 == nil { if cv1 == nil || cv2 == nil {

View file

@ -244,6 +244,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
disabledCh: make(chan struct{}), disabledCh: make(chan struct{}),
exportFileName: config.ExportFileName, exportFileName: config.ExportFileName,
Params: params, Params: params,
targetView: initView,
indexedView: initView,
indexedRange: filterMapsRange{ indexedRange: filterMapsRange{
initialized: initialized, initialized: initialized,
headIndexed: rs.HeadIndexed, headIndexed: rs.HeadIndexed,
@ -265,16 +267,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows), baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots), renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
} }
f.checkRevertRange() // revert maps that are inconsistent with the current chain view
// Set initial indexer target.
f.targetView = initView
if f.indexedRange.initialized {
f.indexedView = f.initChainView(f.targetView)
f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.HeadNumber()+1
if !f.indexedRange.headIndexed {
f.indexedRange.headDelimiter = 0
}
}
if f.indexedRange.hasIndexedBlocks() { if f.indexedRange.hasIndexedBlocks() {
log.Info("Initialized log indexer", log.Info("Initialized log indexer",
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
@ -303,29 +297,40 @@ func (f *FilterMaps) Stop() {
f.closeWg.Wait() f.closeWg.Wait()
} }
// initChainView returns a chain view consistent with both the current target // checkRevertRange checks whether the existing index is consistent with the
// view and the current state of the log index as found in the database, based // current indexed view and reverts inconsistent maps if necessary.
// on the last block of stored maps. func (f *FilterMaps) checkRevertRange() {
// Note that the returned view might be shorter than the existing index if if f.indexedRange.maps.Count() == 0 {
// the latest maps are not consistent with targetView. return
func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView {
mapIndex := f.indexedRange.maps.AfterLast()
for {
var ok bool
mapIndex, ok = f.lastMapBoundaryBefore(mapIndex)
if !ok {
break
} }
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(mapIndex) lastMap := f.indexedRange.maps.Last()
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(lastMap)
if err != nil { if err != nil {
log.Error("Could not initialize indexed chain view", "error", err) log.Error("Error initializing log index database; resetting log index", "error", err)
break f.reset()
return
} }
if lastBlockNumber <= chainView.HeadNumber() && chainView.BlockId(lastBlockNumber) == lastBlockId { for lastBlockNumber > f.indexedView.HeadNumber() || f.indexedView.BlockId(lastBlockNumber) != lastBlockId {
return chainView.limitedView(lastBlockNumber) // revert last map
if f.indexedRange.maps.Count() == 1 {
f.reset() // reset database if no rendered maps remained
return
} }
lastMap--
newRange := f.indexedRange
newRange.maps.SetLast(lastMap)
lastBlockNumber, lastBlockId, err = f.getLastBlockOfMap(lastMap)
if err != nil {
log.Error("Error initializing log index database; resetting log index", "error", err)
f.reset()
return
}
newRange.blocks.SetAfterLast(lastBlockNumber) // lastBlockNumber is probably partially indexed
newRange.headIndexed = false
newRange.headDelimiter = 0
// only shorten range and leave map data; next head render will overwrite it
f.setRange(f.db, f.indexedView, newRange, false)
} }
return chainView.limitedView(0)
} }
// reset un-initializes the FilterMaps structure and removes all related data from // reset un-initializes the FilterMaps structure and removes all related data from