core/filtermaps: export ChainView functions, add GetHeader

This commit is contained in:
Zsolt Felfoldi 2025-04-19 00:27:18 +02:00
parent 4c9e7d1b18
commit 03e88a8505
5 changed files with 68 additions and 46 deletions

View file

@ -58,31 +58,41 @@ func NewChainView(chain blockchain, number uint64, hash common.Hash) *ChainView
return cv return cv
} }
// getBlockHash returns the block hash belonging to the given block number. // HeadNumber returns the head block number of the chain view.
// Note that the hash of the head block is not returned because ChainView might func (cv *ChainView) HeadNumber() uint64 {
// represent a view where the head block is currently being created. return cv.headNumber
func (cv *ChainView) getBlockHash(number uint64) common.Hash {
if number >= cv.headNumber {
panic("invalid block number")
}
return cv.blockHash(number)
} }
// getBlockId returns the unique block id belonging to the given block number. // BlockHash returns the block hash belonging to the given block number.
// Note that it is currently equal to the block hash. In the future it might // Note that the hash of the head block is not returned because ChainView might
// be a different id for future blocks if the log index root becomes part of // represent a view where the head block is currently being created.
// consensus and therefore rendering the index with the new head will happen func (cv *ChainView) BlockHash(number uint64) common.Hash {
// before the hash of that new head is available.
func (cv *ChainView) getBlockId(number uint64) common.Hash {
if number > cv.headNumber { if number > cv.headNumber {
panic("invalid block number") panic("invalid block number")
} }
return cv.blockHash(number) return cv.blockHash(number)
} }
// getReceipts returns the set of receipts belonging to the block at the given // BlockId returns the unique block id belonging to the given block number.
// Note that it is currently equal to the block hash. In the future it might
// be a different id for future blocks if the log index root becomes part of
// consensus and therefore rendering the index with the new head will happen
// before the hash of that new head is available.
func (cv *ChainView) BlockId(number uint64) common.Hash {
if number > cv.headNumber {
panic("invalid block number")
}
return cv.blockHash(number)
}
// Header returns the block header at the given block number.
func (cv *ChainView) Header(number uint64) *types.Header {
return cv.chain.GetHeader(cv.BlockHash(number), number)
}
// Receipts returns the set of receipts belonging to the block at the given
// block number. // block number.
func (cv *ChainView) getReceipts(number uint64) types.Receipts { func (cv *ChainView) Receipts(number uint64) types.Receipts {
if number > cv.headNumber { if number > cv.headNumber {
panic("invalid block number") panic("invalid block number")
} }
@ -93,6 +103,18 @@ func (cv *ChainView) getReceipts(number uint64) types.Receipts {
return cv.chain.GetReceiptsByHash(blockHash) return cv.chain.GetReceiptsByHash(blockHash)
} }
// SharedRange returns the block range shared by two chain views.
func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
if cv == nil || cv2 == nil {
return common.Range[uint64]{}
}
var sharedLen uint64
for n := min(cv.headNumber+1-uint64(len(cv.hashes)), cv2.headNumber+1-uint64(len(cv2.hashes))); n <= cv.headNumber && n <= cv2.headNumber && cv.blockHash(n) == cv2.blockHash(n); n++ {
sharedLen = n + 1
}
return common.NewRange(0, sharedLen)
}
// limitedView returns a new chain view that is a truncated version of the parent view. // limitedView returns a new chain view that is a truncated version of the parent view.
func (cv *ChainView) limitedView(newHead uint64) *ChainView { func (cv *ChainView) limitedView(newHead uint64) *ChainView {
if newHead >= cv.headNumber { if newHead >= cv.headNumber {
@ -106,7 +128,7 @@ func equalViews(cv1, cv2 *ChainView) bool {
if cv1 == nil || cv2 == nil { if cv1 == nil || cv2 == nil {
return false return false
} }
return cv1.headNumber == cv2.headNumber && cv1.getBlockId(cv1.headNumber) == cv2.getBlockId(cv2.headNumber) return cv1.headNumber == cv2.headNumber && cv1.BlockId(cv1.headNumber) == cv2.BlockId(cv2.headNumber)
} }
// matchViews returns true if the two chain views are equivalent up until the // matchViews returns true if the two chain views are equivalent up until the
@ -120,9 +142,9 @@ func matchViews(cv1, cv2 *ChainView, number uint64) bool {
return false return false
} }
if number == cv1.headNumber || number == cv2.headNumber { if number == cv1.headNumber || number == cv2.headNumber {
return cv1.getBlockId(number) == cv2.getBlockId(number) return cv1.BlockId(number) == cv2.BlockId(number)
} }
return cv1.getBlockHash(number) == cv2.getBlockHash(number) return cv1.BlockHash(number) == cv2.BlockHash(number)
} }
// extendNonCanonical checks whether the previously known reverse list of head // extendNonCanonical checks whether the previously known reverse list of head

View file

@ -262,7 +262,7 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
f.targetView = initView f.targetView = initView
if f.indexedRange.initialized { if f.indexedRange.initialized {
f.indexedView = f.initChainView(f.targetView) f.indexedView = f.initChainView(f.targetView)
f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.headNumber+1 f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.HeadNumber()+1
if !f.indexedRange.headIndexed { if !f.indexedRange.headIndexed {
f.indexedRange.headDelimiter = 0 f.indexedRange.headDelimiter = 0
} }
@ -313,7 +313,7 @@ func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView {
log.Error("Could not initialize indexed chain view", "error", err) log.Error("Could not initialize indexed chain view", "error", err)
break break
} }
if lastBlockNumber <= chainView.headNumber && chainView.getBlockId(lastBlockNumber) == lastBlockId { if lastBlockNumber <= chainView.HeadNumber() && chainView.BlockId(lastBlockNumber) == lastBlockId {
return chainView.limitedView(lastBlockNumber) return chainView.limitedView(lastBlockNumber)
} }
} }
@ -370,7 +370,7 @@ func (f *FilterMaps) init() error {
for min < max { for min < max {
mid := (min + max + 1) / 2 mid := (min + max + 1) / 2
cp := checkpointList[mid-1] cp := checkpointList[mid-1]
if cp.BlockNumber <= f.targetView.headNumber && f.targetView.getBlockId(cp.BlockNumber) == cp.BlockId { if cp.BlockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(cp.BlockNumber) == cp.BlockId {
min = mid min = mid
} else { } else {
max = mid - 1 max = mid - 1
@ -512,7 +512,7 @@ func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) {
} }
} }
// get block receipts // get block receipts
receipts := f.indexedView.getReceipts(firstBlockNumber) receipts := f.indexedView.Receipts(firstBlockNumber)
if receipts == nil { if receipts == nil {
return nil, fmt.Errorf("failed to retrieve receipts for block %d containing searched log value index %d: %v", firstBlockNumber, lvIndex, err) return nil, fmt.Errorf("failed to retrieve receipts for block %d containing searched log value index %d: %v", firstBlockNumber, lvIndex, err)
} }

View file

@ -44,7 +44,7 @@ func (f *FilterMaps) indexerLoop() {
for !f.stop { for !f.stop {
if !f.indexedRange.initialized { if !f.indexedRange.initialized {
if f.targetView.headNumber == 0 { if f.targetView.HeadNumber() == 0 {
// initialize when chain head is available // initialize when chain head is available
f.processSingleEvent(true) f.processSingleEvent(true)
continue continue
@ -249,7 +249,7 @@ func (f *FilterMaps) tryIndexHead() error {
log.Info("Log index head rendering in progress", log.Info("Log index head rendering in progress",
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex, "processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
"remaining", f.indexedView.headNumber-f.indexedRange.blocks.Last(), "remaining", f.indexedView.HeadNumber()-f.indexedRange.blocks.Last(),
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt))) "elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
f.loggedHeadIndex = true f.loggedHeadIndex = true
f.lastLogHeadIndex = time.Now() f.lastLogHeadIndex = time.Now()
@ -418,10 +418,10 @@ func (f *FilterMaps) needTailEpoch(epoch uint32) bool {
// tailTargetBlock returns the target value for the tail block number according // tailTargetBlock returns the target value for the tail block number according
// to the log history parameter and the current index head. // to the log history parameter and the current index head.
func (f *FilterMaps) tailTargetBlock() uint64 { func (f *FilterMaps) tailTargetBlock() uint64 {
if f.history == 0 || f.indexedView.headNumber < f.history { if f.history == 0 || f.indexedView.HeadNumber() < f.history {
return 0 return 0
} }
return f.indexedView.headNumber + 1 - f.history return f.indexedView.HeadNumber() + 1 - f.history
} }
// tailPartialBlocks returns the number of rendered blocks in the partially // tailPartialBlocks returns the number of rendered blocks in the partially

View file

@ -143,8 +143,8 @@ func (f *FilterMaps) lastCanonicalSnapshotOfMap(mapIndex uint32) *renderedMap {
var best *renderedMap var best *renderedMap
for _, blockNumber := range f.renderSnapshots.Keys() { for _, blockNumber := range f.renderSnapshots.Keys() {
if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() && if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() &&
blockNumber <= f.indexedView.headNumber && f.indexedView.getBlockId(blockNumber) == cp.lastBlockId && blockNumber <= f.indexedView.HeadNumber() && f.indexedView.BlockId(blockNumber) == cp.lastBlockId &&
blockNumber <= f.targetView.headNumber && f.targetView.getBlockId(blockNumber) == cp.lastBlockId && blockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(blockNumber) == cp.lastBlockId &&
cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) { cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) {
best = cp best = cp
} }
@ -173,7 +173,7 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore uint32) (nextMa
return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err) return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err)
} }
if (f.indexedRange.headIndexed && mapIndex >= f.indexedRange.maps.Last()) || if (f.indexedRange.headIndexed && mapIndex >= f.indexedRange.maps.Last()) ||
lastBlock >= f.targetView.headNumber || lastBlockId != f.targetView.getBlockId(lastBlock) { lastBlock >= f.targetView.HeadNumber() || lastBlockId != f.targetView.BlockId(lastBlock) {
continue // map is not full or inconsistent with targetView; roll back continue // map is not full or inconsistent with targetView; roll back
} }
lvPtr, err := f.getBlockLvPointer(lastBlock) lvPtr, err := f.getBlockLvPointer(lastBlock)
@ -247,7 +247,7 @@ func (f *FilterMaps) loadHeadSnapshot() error {
filterMap: fm, filterMap: fm,
mapIndex: f.indexedRange.maps.Last(), mapIndex: f.indexedRange.maps.Last(),
lastBlock: f.indexedRange.blocks.Last(), lastBlock: f.indexedRange.blocks.Last(),
lastBlockId: f.indexedView.getBlockId(f.indexedRange.blocks.Last()), lastBlockId: f.indexedView.BlockId(f.indexedRange.blocks.Last()),
blockLvPtrs: lvPtrs, blockLvPtrs: lvPtrs,
finished: true, finished: true,
headDelimiter: f.indexedRange.headDelimiter, headDelimiter: f.indexedRange.headDelimiter,
@ -264,7 +264,7 @@ func (r *mapRenderer) makeSnapshot() {
filterMap: r.currentMap.filterMap.fastCopy(), filterMap: r.currentMap.filterMap.fastCopy(),
mapIndex: r.currentMap.mapIndex, mapIndex: r.currentMap.mapIndex,
lastBlock: r.currentMap.lastBlock, lastBlock: r.currentMap.lastBlock,
lastBlockId: r.iterator.chainView.getBlockId(r.currentMap.lastBlock), lastBlockId: r.iterator.chainView.BlockId(r.currentMap.lastBlock),
blockLvPtrs: r.currentMap.blockLvPtrs, blockLvPtrs: r.currentMap.blockLvPtrs,
finished: true, finished: true,
headDelimiter: r.iterator.lvIndex, headDelimiter: r.iterator.lvIndex,
@ -370,7 +370,7 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) {
r.currentMap.finished = true r.currentMap.finished = true
r.currentMap.headDelimiter = r.iterator.lvIndex r.currentMap.headDelimiter = r.iterator.lvIndex
} }
r.currentMap.lastBlockId = r.f.targetView.getBlockId(r.currentMap.lastBlock) r.currentMap.lastBlockId = r.f.targetView.BlockId(r.currentMap.lastBlock)
totalTime += time.Since(start) totalTime += time.Since(start)
mapRenderTimer.Update(totalTime) mapRenderTimer.Update(totalTime)
mapLogValueMeter.Mark(logValuesProcessed) mapLogValueMeter.Mark(logValuesProcessed)
@ -566,8 +566,8 @@ func (r *mapRenderer) getUpdatedRange() (filterMapsRange, error) {
lm := r.finishedMaps[r.finished.Last()] lm := r.finishedMaps[r.finished.Last()]
newRange.headIndexed = lm.finished newRange.headIndexed = lm.finished
if lm.finished { if lm.finished {
newRange.blocks.SetLast(r.f.targetView.headNumber) newRange.blocks.SetLast(r.f.targetView.HeadNumber())
if lm.lastBlock != r.f.targetView.headNumber { if lm.lastBlock != r.f.targetView.HeadNumber() {
panic("map rendering finished but last block != head block") panic("map rendering finished but last block != head block")
} }
newRange.headDelimiter = lm.headDelimiter newRange.headDelimiter = lm.headDelimiter
@ -665,13 +665,13 @@ var errUnindexedRange = errors.New("unindexed range")
// given block's first log value entry (the block delimiter), according to the // given block's first log value entry (the block delimiter), according to the
// current targetView. // current targetView.
func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint64) (*logIterator, error) { func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint64) (*logIterator, error) {
if blockNumber > f.targetView.headNumber { if blockNumber > f.targetView.HeadNumber() {
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.headNumber) return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.HeadNumber())
} }
if !f.indexedRange.blocks.Includes(blockNumber) { if !f.indexedRange.blocks.Includes(blockNumber) {
return nil, errUnindexedRange return nil, errUnindexedRange
} }
finished := blockNumber == f.targetView.headNumber finished := blockNumber == f.targetView.HeadNumber()
l := &logIterator{ l := &logIterator{
chainView: f.targetView, chainView: f.targetView,
params: &f.Params, params: &f.Params,
@ -687,11 +687,11 @@ func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint6
// newLogIteratorFromMapBoundary creates a logIterator starting at the given // newLogIteratorFromMapBoundary creates a logIterator starting at the given
// map boundary, according to the current targetView. // map boundary, according to the current targetView.
func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, startLvPtr uint64) (*logIterator, error) { func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, startLvPtr uint64) (*logIterator, error) {
if startBlock > f.targetView.headNumber { if startBlock > f.targetView.HeadNumber() {
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.headNumber) return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.HeadNumber())
} }
// get block receipts // get block receipts
receipts := f.targetView.getReceipts(startBlock) receipts := f.targetView.Receipts(startBlock)
if receipts == nil { if receipts == nil {
return nil, fmt.Errorf("receipts not found for start block %d", startBlock) return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
} }
@ -758,7 +758,7 @@ func (l *logIterator) next() error {
if l.delimiter { if l.delimiter {
l.delimiter = false l.delimiter = false
l.blockNumber++ l.blockNumber++
l.receipts = l.chainView.getReceipts(l.blockNumber) l.receipts = l.chainView.Receipts(l.blockNumber)
if l.receipts == nil { if l.receipts == nil {
return fmt.Errorf("receipts not found for block %d", l.blockNumber) return fmt.Errorf("receipts not found for block %d", l.blockNumber)
} }
@ -795,7 +795,7 @@ func (l *logIterator) enforceValidState() {
} }
l.logIndex = 0 l.logIndex = 0
} }
if l.blockNumber == l.chainView.headNumber { if l.blockNumber == l.chainView.HeadNumber() {
l.finished = true l.finished = true
} else { } else {
l.delimiter = true l.delimiter = true

View file

@ -128,7 +128,7 @@ func (fm *FilterMapsMatcherBackend) synced() {
indexedBlocks.SetAfterLast(indexedBlocks.Last()) // remove partially indexed last block indexedBlocks.SetAfterLast(indexedBlocks.Last()) // remove partially indexed last block
} }
fm.syncCh <- SyncRange{ fm.syncCh <- SyncRange{
HeadNumber: fm.f.targetView.headNumber, HeadNumber: fm.f.targetView.HeadNumber(),
ValidBlocks: fm.validBlocks, ValidBlocks: fm.validBlocks,
IndexedBlocks: indexedBlocks, IndexedBlocks: indexedBlocks,
} }
@ -154,7 +154,7 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange
case <-ctx.Done(): case <-ctx.Done():
return SyncRange{}, ctx.Err() return SyncRange{}, ctx.Err()
case <-fm.f.disabledCh: case <-fm.f.disabledCh:
return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil return SyncRange{HeadNumber: fm.f.targetView.HeadNumber()}, nil
} }
select { select {
case vr := <-syncCh: case vr := <-syncCh:
@ -162,7 +162,7 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange
case <-ctx.Done(): case <-ctx.Done():
return SyncRange{}, ctx.Err() return SyncRange{}, ctx.Err()
case <-fm.f.disabledCh: case <-fm.f.disabledCh:
return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil return SyncRange{HeadNumber: fm.f.targetView.HeadNumber()}, nil
} }
} }