From 2961e7285fa5bbd90d4ad78ba23f2b1021b0621a Mon Sep 17 00:00:00 2001 From: Zsolt Felfoldi Date: Sat, 15 Mar 2025 12:02:06 +0100 Subject: [PATCH] added comments --- common/range.go | 18 ++++++++++++++++++ eth/filters/filter.go | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/common/range.go b/common/range.go index a346998bb8..63c7e2a0b1 100644 --- a/common/range.go +++ b/common/range.go @@ -28,10 +28,12 @@ type Range[T uint32 | uint64] struct { first, afterLast T } +// NewRange creates a new range based of first element and number of elements. func NewRange[T uint32 | uint64](first, count T) Range[T] { return Range[T]{first, first + count} } +// EncodeRLP implements rlp.Encoder. func (r *Range[T]) EncodeRLP(w io.Writer) error { if err := rlp.Encode(w, &r.first); err != nil { return err @@ -39,6 +41,7 @@ func (r *Range[T]) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &r.afterLast) } +// DecodeRLP implements rlp.Decoder. func (r *Range[T]) DecodeRLP(s *rlp.Stream) error { if err := s.Decode(&r.first); err != nil { return err @@ -46,10 +49,13 @@ func (r *Range[T]) DecodeRLP(s *rlp.Stream) error { return s.Decode(&r.afterLast) } +// First returns the first element of the range. func (r Range[T]) First() T { return r.first } +// Last returns the last element of the range. The function panics if the range +// is empty. func (r Range[T]) Last() T { if r.first == r.afterLast { panic("last item of zero length range is not allowed") @@ -57,22 +63,28 @@ func (r Range[T]) Last() T { return r.afterLast - 1 } +// AfterLast returns the first element after the range. This allows obtaining +// information about the end part of zero length ranges. func (r Range[T]) AfterLast() T { return r.afterLast } +// Count returns the number of elements in the range. func (r Range[T]) Count() T { return r.afterLast - r.first } +// IsEmpty returns true if the range is empty. func (r Range[T]) IsEmpty() bool { return r.first == r.afterLast } +// Includes returns true if the given element is inside the range. func (r Range[T]) Includes(v T) bool { return v >= r.first && v < r.afterLast } +// SetFirst updates the first element of the list. func (r *Range[T]) SetFirst(v T) { r.first = v if r.afterLast < r.first { @@ -80,6 +92,8 @@ func (r *Range[T]) SetFirst(v T) { } } +// SetAfterLast updates the end of the range by specifying the first element +// after the range. This allows setting zero length ranges. func (r *Range[T]) SetAfterLast(v T) { r.afterLast = v if r.afterLast < r.first { @@ -87,10 +101,12 @@ func (r *Range[T]) SetAfterLast(v T) { } } +// SetLast updates last element of the range. func (r *Range[T]) SetLast(v T) { r.SetAfterLast(v + 1) } +// Intersection returns the intersection of two ranges. func (r Range[T]) Intersection(q Range[T]) Range[T] { i := Range[T]{first: max(r.first, q.first), afterLast: min(r.afterLast, q.afterLast)} if i.first > i.afterLast { @@ -99,6 +115,8 @@ func (r Range[T]) Intersection(q Range[T]) Range[T] { return i } +// Union returns the union of two ranges. The function panics if there is a gap +// between the ranges. func (r Range[T]) Union(q Range[T]) Range[T] { if max(r.first, q.first) > min(r.afterLast, q.afterLast) { panic("cannot create union; gap between ranges") diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 2a95500ea5..5efdfda727 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -146,6 +146,7 @@ type rangeLogsTestEvent struct { begin, end uint64 } +// searchSession represents a single search session. type searchSession struct { ctx context.Context filter *Filter @@ -158,6 +159,7 @@ type searchSession struct { forceUnindexed bool // revert to unindexed search } +// newSearchSession returns a new searchSession. func newSearchSession(ctx context.Context, filter *Filter, mb filtermaps.MatcherBackend, firstBlock, lastBlock uint64) (*searchSession, error) { s := &searchSession{ ctx: ctx, @@ -174,6 +176,20 @@ func newSearchSession(ctx context.Context, filter *Filter, mb filtermaps.Matcher return s, nil } +// syncMatcher performs a synchronization step with the matcher. The resulting +// syncRange structure holds information about the latest range of indexed blocks +// and the guaranteed valid blocks whose log index have not been changed since +// the previous synchronization. +// The function also performs trimming of the match set in order to always keep +// it consistent with the synced matcher state. +// Tail trimming is only performed if the first block of the valid log index range +// is higher than trimTailThreshold. This is useful because unindexed log search +// is not affected by the valid tail (on the other hand, valid head is taken into +// account in order to provide reorg safety, even though the log index is not used). +// In case of indexed search the tail is only trimmed if the first part of the +// recently obtained results might be invalid. If guaranteed valid new results +// have been added at the head of previously validated results then there is no +// need to discard those even if the index tail have been unindexed since that. func (s *searchSession) syncMatcher(trimTailThreshold uint64) error { if s.filter.rangeLogsTestHook != nil && !s.matchRange.IsEmpty() { s.filter.rangeLogsTestHook <- rangeLogsTestEvent{event: rangeLogsTestSync, begin: s.matchRange.First(), end: s.matchRange.Last()} @@ -206,6 +222,8 @@ func (s *searchSession) syncMatcher(trimTailThreshold uint64) error { return nil } +// trimMatches removes any entries from the current set of matches that is outside +// the given range. func (s *searchSession) trimMatches(trimRange common.Range[uint64]) { s.matchRange = s.matchRange.Intersection(trimRange) if s.matchRange.IsEmpty() { @@ -220,6 +238,7 @@ func (s *searchSession) trimMatches(trimRange common.Range[uint64]) { } } +// searchInRange performs a single range search, either indexed or unindexed. func (s *searchSession) searchInRange(r common.Range[uint64], indexed bool) ([]*types.Log, error) { first, last := r.First(), r.Last() if indexed { @@ -241,6 +260,8 @@ func (s *searchSession) searchInRange(r common.Range[uint64], indexed bool) ([]* return s.filter.unindexedLogs(s.ctx, first, last) } +// doSearchIteration performs a search on a range missing from an incomplete set +// of results, adds the new section and removes invalidated entries. func (s *searchSession) doSearchIteration() error { switch { case s.syncRange.IndexedBlocks.IsEmpty():