Skip to content

feat: handle reorgs in get_filter_changes with reorg watermark#98

Draft
prestwich wants to merge 1 commit intojames/eng-1970from
james/eng-1971
Draft

feat: handle reorgs in get_filter_changes with reorg watermark#98
prestwich wants to merge 1 commit intojames/eng-1970from
james/eng-1971

Conversation

@prestwich
Copy link
Member

@prestwich prestwich commented Mar 10, 2026

Summary

  • Adds reorg_watermark: Option<u64> to ActiveFilter so filters can rewind next_start_block when a reorg invalidates their polling window
  • Spawns a FilterReorgTask that subscribes to ChainEvent::Reorg broadcasts and eagerly propagates watermarks to all active filters
  • Updates get_filter_changes to check the watermark before querying, plus an implicit fallback check (latest < start) for missed broadcasts
  • Adds unit tests for watermark semantics

Closes ENG-1971

Stack

This PR includes commits from #96 and #97. Review only the top commit.

  1. feat: update BlockTags during reorgs to prevent stale tag window #96BlockTags::rewind_to for reorg tag updates
  2. feat: handle ChainEvent::Reorg in SubscriptionTask #97SubscriptionTask reorg handling
  3. feat: handle reorgs in get_filter_changes with reorg watermark #98 ← this PRget_filter_changes reorg watermark
  4. test: integration tests for reorg tracking in RPC subscriptions and filters #99 — Integration tests (includes feat: update BlockTags during reorgs to prevent stale tag window #96, feat: handle ChainEvent::Reorg in SubscriptionTask #97, feat: handle reorgs in get_filter_changes with reorg watermark #98)

Test plan

  • cargo clippy -p signet-rpc --all-features --all-targets — clean
  • cargo +nightly fmt — clean
  • cargo t -p signet-rpc — 35 tests + 4 doc-tests pass
  • Review that FilterReorgTask self-terminates when FilterManager is dropped (uses Weak)
  • Review implicit reorg detection edge cases

🤖 Generated with Claude Code

@prestwich prestwich requested a review from a team as a code owner March 10, 2026 12:03
Copy link
Member

@Evalir Evalir left a comment

Choose a reason for hiding this comment

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

I think this is fine. It requires thinking quite a bit through the possible reorg cases but the flow is clear to me. I'm good with filter changes returning the empty output instead of querying an impossible range

Comment on lines +133 to +137
pub fn rewind_to(&self, ancestor: u64) {
self.latest.fetch_min(ancestor, Ordering::Release);
self.safe.fetch_min(ancestor, Ordering::Release);
self.finalized.fetch_min(ancestor, Ordering::Release);
}
Copy link
Member

Choose a reason for hiding this comment

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

this order should be fine, since finalized should actually not be possible to move (else things went very, very wrong (or you are unichain))

Add a `reorg_watermark` field to `ActiveFilter` that records the common
ancestor block when a chain reorganization occurs. `FilterManager` now
subscribes to `ChainEvent::Reorg` broadcasts and eagerly propagates
watermarks to all active filters. On the next poll, `get_filter_changes`
rewinds `next_start_block` so re-fetched data reflects the new chain.

An implicit reorg detection check (latest < start) provides a
belt-and-suspenders fallback when the explicit watermark is missed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@prestwich prestwich changed the base branch from develop to james/eng-1970 March 11, 2026 13:12
@Evalir Evalir self-requested a review March 11, 2026 13:20
let ChainEvent::Reorg(reorg) = event else { continue };

let Some(manager) = self.manager.upgrade() else { break };
manager.set_reorg_watermark_all(reorg.common_ancestor);
Copy link
Member

Choose a reason for hiding this comment

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

hmm, this will set a watermark for all filters alive, but these filters weren't necessarily alive when the reorg event happened? due to scheduling, this task could run after a reorg happened, and then a filter was created. This would then set an invalid watermark for this new filter, no?

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.

2 participants