Skip to content

Conversation

@antonis19
Copy link
Member

Follow-up to : #16683

The previous PR relied on the assumption that span.StartBlock is monotonically increasing, but it turns out that assumption is not true due to a future span being announced in advance from Heimdall and a span rotation taking place before the announced span.

e.g.

var spans = []Span{
		Span{
			Id:         0,
			StartBlock: 0,
			EndBlock:   999,
		},
		Span{
			Id:         1, // new span announced
			StartBlock: 1000,
			EndBlock:   1999,
		},
		Span{
			Id:         2, // span rotation
			StartBlock: 5,
			EndBlock:   1999,
		},
	}

Therefore, this PR adapts the lookup algorithm by starting the search from entry := cursor.Seek(blockNum) and walking the BorSpans table backwards using cursor.Prev() while recording the highest span id which is in range, until an out-of-range span is reached, at which point we break and return the highest span id seen.

This lookup algorithm relies on the assumption that there won't ever be more than 2 consecutive spans for which span1.Id < span2.Id and span1.StartBlock > span2.StartBlock . If this assumption were ever to be broken, we would have to fall back on brute-force linear search.

Also, another detail to take into account is that due to the span rotations multiple spans with the same StartBlock could be produced, therefore overwriting the value of the index table at that block number. This is not a problem, because if this happens, then the previous span value will become entirely obsolete and no block number could have the old span associated with it.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR updates the span index lookup algorithm to handle span rotations where StartBlock values are not monotonically increasing. The previous implementation relied on the assumption that spans would always have increasing start blocks, but this fails when Heimdall announces future spans before span rotations occur.

Key changes:

  • Modified lookup algorithm to walk backwards from the seek position while tracking the highest span ID in range
  • Added comprehensive unit tests covering complex span rotation scenarios
  • Fixed assertion parameter order in existing tests

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
polygon/heimdall/span_range_index.go Updated lookup algorithm to handle non-monotonic StartBlock values by walking backwards and finding highest span ID
polygon/heimdall/span_range_index_test.go Added extensive test cases for span rotations and fixed assertion parameter order
polygon/heimdall/snapshot_store_test.go Fixed assertion parameter order and updated test data to match new span rotation scenarios

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

if isInRange && prevSpanId > lastSpanIdInRange { // a span in range with higher span id was found
lastSpanIdInRange = prevSpanId
}
}
Copy link

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function returns true even when lastSpanIdInRange is 0 (the initial value), which indicates no valid span was found. This should return false when no span is found in range.

Suggested change
}
}
if lastSpanIdInRange == 0 {
return 0, false, nil
}

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there was no span truly in range, then this part of the code would not even be reached, instead the code above it dealing with cursor.Last() would be triggered instead.

@antonis19 antonis19 merged commit 924bc4d into main Aug 20, 2025
15 checks passed
@antonis19 antonis19 deleted the spans-index-lookup branch August 20, 2025 10:15
antonis19 added a commit that referenced this pull request Aug 20, 2025
Follow-up to : #16683

The previous PR relied on the assumption that `span.StartBlock` is
monotonically increasing, but it turns out that assumption is not true
due to a future span being announced in advance from Heimdall and a span
rotation taking place before the announced span.

e.g.

```Go
var spans = []Span{
		Span{
			Id:         0,
			StartBlock: 0,
			EndBlock:   999,
		},
		Span{
			Id:         1, // new span announced
			StartBlock: 1000,
			EndBlock:   1999,
		},
		Span{
			Id:         2, // span rotation
			StartBlock: 5,
			EndBlock:   1999,
		},
	}
```

Therefore, this PR adapts the lookup algorithm by starting the search
from `entry := cursor.Seek(blockNum)` and walking the `BorSpans` table
backwards using `cursor.Prev()` while recording the highest span id
which is in range, until an out-of-range span is reached, at which point
we break and return the highest span id seen.

This lookup algorithm relies on the assumption that there won't ever be
more than 2 consecutive spans for which `span1.Id < span2.Id` and
`span1.StartBlock > span2.StartBlock` . If this assumption were ever to
be broken, we would have to fall back on brute-force linear search.

Also, another detail to take into account is that due to the span
rotations multiple spans with the same `StartBlock` could be produced,
therefore overwriting the value of the index table at that block number.
This is not a problem, because if this happens, then the previous span
value will become entirely obsolete and no block number could have the
old span associated with it.

---------

Co-authored-by: antonis19 <[email protected]>
antonis19 added a commit that referenced this pull request Aug 20, 2025
Cherry-picks for :
- #16404
- #16683
- #16729

---------

Co-authored-by: antonis19 <[email protected]>
antonis19 added a commit that referenced this pull request Sep 11, 2025
Follow-up to : #16683

The previous PR relied on the assumption that `span.StartBlock` is
monotonically increasing, but it turns out that assumption is not true
due to a future span being announced in advance from Heimdall and a span
rotation taking place before the announced span.

e.g.

```Go
var spans = []Span{
		Span{
			Id:         0,
			StartBlock: 0,
			EndBlock:   999,
		},
		Span{
			Id:         1, // new span announced
			StartBlock: 1000,
			EndBlock:   1999,
		},
		Span{
			Id:         2, // span rotation
			StartBlock: 5,
			EndBlock:   1999,
		},
	}
```

Therefore, this PR adapts the lookup algorithm by starting the search
from `entry := cursor.Seek(blockNum)` and walking the `BorSpans` table
backwards using `cursor.Prev()` while recording the highest span id
which is in range, until an out-of-range span is reached, at which point
we break and return the highest span id seen.

This lookup algorithm relies on the assumption that there won't ever be
more than 2 consecutive spans for which `span1.Id < span2.Id` and
`span1.StartBlock > span2.StartBlock` . If this assumption were ever to
be broken, we would have to fall back on brute-force linear search.

Also, another detail to take into account is that due to the span
rotations multiple spans with the same `StartBlock` could be produced,
therefore overwriting the value of the index table at that block number.
This is not a problem, because if this happens, then the previous span
value will become entirely obsolete and no block number could have the
old span associated with it.

---------

Co-authored-by: antonis19 <[email protected]>
yperbasis pushed a commit that referenced this pull request Sep 12, 2025
NazariiDenha pushed a commit that referenced this pull request Oct 24, 2025
Follow-up to : #16683

The previous PR relied on the assumption that `span.StartBlock` is
monotonically increasing, but it turns out that assumption is not true
due to a future span being announced in advance from Heimdall and a span
rotation taking place before the announced span.

e.g.

```Go
var spans = []Span{
		Span{
			Id:         0,
			StartBlock: 0,
			EndBlock:   999,
		},
		Span{
			Id:         1, // new span announced
			StartBlock: 1000,
			EndBlock:   1999,
		},
		Span{
			Id:         2, // span rotation
			StartBlock: 5,
			EndBlock:   1999,
		},
	}
```

Therefore, this PR adapts the lookup algorithm by starting the search
from `entry := cursor.Seek(blockNum)` and walking the `BorSpans` table
backwards using `cursor.Prev()` while recording the highest span id
which is in range, until an out-of-range span is reached, at which point
we break and return the highest span id seen.

This lookup algorithm relies on the assumption that there won't ever be
more than 2 consecutive spans for which `span1.Id < span2.Id` and
`span1.StartBlock > span2.StartBlock` . If this assumption were ever to
be broken, we would have to fall back on brute-force linear search.

Also, another detail to take into account is that due to the span
rotations multiple spans with the same `StartBlock` could be produced,
therefore overwriting the value of the index table at that block number.
This is not a problem, because if this happens, then the previous span
value will become entirely obsolete and no block number could have the
old span associated with it.

---------

Co-authored-by: antonis19 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants