Skip to content

fix: Fix eth_getLogs pending state handling, and more e2e pending state coverage#267

Merged
louisliu2048 merged 4 commits intomainfrom
niven/add-fb-tests
Apr 24, 2026
Merged

fix: Fix eth_getLogs pending state handling, and more e2e pending state coverage#267
louisliu2048 merged 4 commits intomainfrom
niven/add-fb-tests

Conversation

@sieniven
Copy link
Copy Markdown
Contributor

@sieniven sieniven commented Apr 24, 2026

Description

Two related changes to the flashblocks RPC layer.

Fix eth_getLogs pending-state handling (crates/rpc/src/filter.rs)

toBlock=Pending/Latest delegated the whole filter to the canonical filter whenever get_rpc_block returned None — which happens in the brief window after a pending sequence is promoted (pending_cache = None) or after canonical catch-up evicts the confirm-cache entry. The canonical filter then resolves Pending/Latest against canonical and drops logs from confirm-cache blocks ahead of canonical.

Now resolves Pending/Latest directly to pending_height/confirm_height from the flashblocks state cache; the existing range scan handles cross-boundary fallback via get_canonical_logs. Earliest/Safe/Finalized delegate the whole filter to canonical (they are not served by the FB cache), distinguished from "no bound supplied" so the bound is not silently collapsed into the FB cache range.

More e2e pending-state coverage (crates/tests/flashblocks-tests/main.rs)

  • fb_pending_native_balance_delta_test — asserts sender/recipient balance deltas and sender nonce increment on Pending immediately after a native transfer.
  • fb_pending_erc20_balance_delta_test — asserts sender/recipient ERC20 balance deltas on Pending via eth_call(balanceOf) immediately after an ERC20 transfer.
  • sleep_one_blocktime() (~1 block) at the start of pending-state tests that send transactions to avoid block-boundary races.
  • Reworked fb_pending_get_logs_test and fb_get_logs_cross_boundary_range_test to query via Pending / Number(tx_block) ahead-of-canonical ranges instead of waiting for canonical catch-up — exercising the actual flashblocks overlay.

Type of Change

  • Bug fix
  • Refactoring (no functional changes) — simplified FlashblocksEthFilterExt::logs flow

Testing

  • cargo check -p xlayer-rpc, cargo clippy -p xlayer-rpc --all-targets -- -D warnings, cargo fmt -p xlayer-rpc -- --check
  • cargo check/clippy/fmt -p xlayer-e2e-test --test flashblocks_tests
  • E2E: fb_pending_get_logs_test reliably passes against a running devnet, including in the pending_cache = None window that previously caused intermittent failures.

Additional Notes

None — no public API changes outside FlashblocksEthFilterExt::new dropping the unused eth_api parameter (bin/node/src/main.rs updated in the same commit).

sieniven and others added 2 commits April 24, 2026 10:33
Add two new e2e tests that exercise flashblocks pending state ahead of
canonical: fb_pending_native_balance_delta_test verifies sender/recipient
balance deltas and sender nonce increment on Pending immediately after a
native transfer; fb_pending_erc20_balance_delta_test verifies sender and
recipient ERC20 balance deltas on Pending via eth_call(balanceOf).

Add sleep_one_blocktime (~1 block) at the start of pending-state tests
that send transactions so tests do not race with block boundaries, and
add an erc20_balance_of_pending helper that reads balanceOf via manual
calldata to avoid sol! macro name collisions.

Rework fb_pending_get_logs_test and fb_get_logs_cross_boundary_range_test
to query via the Pending tag instead of waiting for the canonical chain
to reach the tx block. Snapshot canonical height before send and assert
that tx_block is ahead of canonical_before so the log is verifiably
served from the flashblocks state cache overlay.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
eth_getLogs on the flashblocks RPC delegated the whole filter to the
canonical filter whenever get_rpc_block returned None for the toBlock
tag. This happened in a brief window after a pending sequence was
promoted (pending_cache = None) or after canonical catch-up evicted the
confirm-cache entry at confirm_height, causing the canonical filter to
resolve Pending/Latest against canonical and drop logs from confirm-
cache blocks ahead of canonical.

Resolve Pending/Latest directly to pending_height/confirm_height from
the flashblocks state cache and let the existing range scan handle
cross-boundary fallback via get_canonical_logs. Distinguish "no bound
supplied" (Option::None) from "unsupported tag"
(Earliest/Safe/Finalized) and delegate the whole filter to canonical
only for the latter, so those tags are not silently collapsed to the FB
cache range. Drop the now-unused eth_api field and its main.rs arg
since the new logic no longer needs convert_block_number.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sieniven
Copy link
Copy Markdown
Contributor Author

@claude review for me

Copy link
Copy Markdown

@XLayer-Bot XLayer-Bot left a comment

Choose a reason for hiding this comment

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

The core filter.rs fix is correct and well-designed. Resolving Pending/Latest directly to the cached heights eliminates the delegation race during the pending_cache = None window, and resolve_fb_bound cleanly separates FB-served tags from canonical-only ones (Earliest/Safe/Finalized). Removing the unused eth_api field is a good clean-up. Two concerns in the new test code below.

workflow run

Comment thread crates/tests/flashblocks-tests/main.rs
Comment thread crates/tests/flashblocks-tests/main.rs
The stale `from > confirm_height → empty` guard short-circuited valid
queries when a tx landed in the currently-building pending sequence
(from = pending_height, confirm_height = pending_height - 1). With
Pending/Latest now resolving to flashblocks cache heights, the pending
block is a valid range bound: its block and receipts are served
directly from the pending sequence in the FB cache.

Drop the stale guard. Keep the existing `from > pending_height → error`
and add a symmetric `to > pending_height → error` so the range scan
only runs for ranges within the flashblocks cache tip.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sieniven
Copy link
Copy Markdown
Contributor Author

@claude review again

Copy link
Copy Markdown

@XLayer-Bot XLayer-Bot left a comment

Choose a reason for hiding this comment

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

Both previously flagged concerns have been fully addressed:

  1. L1 data fee in sender-balance assertion — accepted design: XLayer devnet uses zero L1 fee oracle scalars, so is effectively zero and the simplified gas cost computation is correct.

  2. **Stale guard causing empty-result race in ** — fixed in commit 63fd06e: the guard is removed, now resolves to via , and is a valid range served directly from the FB cache. Only now returns .

The rest of the changes look clean:

  • Removing the now-unused field from is a good simplification.
  • covers all variants correctly (// delegate to canonical).
  • The additions reduce block-boundary flakiness across the pending-state tests.
  • New and are well-structured and correctly account for gas cost.
  • The ABI encoding (selector + 12-byte padding + 20-byte address = 36 bytes) is correct.

workflow run

The 500ms receipt poll interval dominated the measured confirmation
latency in fb_benchmark_*_tx_confirmation tests: flashblocks nodes can
confirm in well under one block time, so every sample rounded up to the
next 500ms tick and the benchmark never reflected true sub-block
latency. 10ms aligns the poll interval below a flashblock slot
(~250ms) so benchmarks report accurate confirmation timings.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@louisliu2048 louisliu2048 merged commit a968f0b into main Apr 24, 2026
@XLayer-Bot
Copy link
Copy Markdown

🔧 CI running — workflow run

@XLayer-Bot
Copy link
Copy Markdown

XLayer Reth CI passed for commit a968f0b2bb962e0a1a02294dbc345e3b1b4a54be

Step Result
format-check ✅ success
compile-check ✅ success
clippy ✅ success
tests ✅ success

View run

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.

4 participants