Skip to content

feat(flashblocks rpc): Full revamp of X Layer custom flashblocks RPC layer#176

Draft
sieniven wants to merge 43 commits intomainfrom
niven/cleanup-flashblocks-rpc
Draft

feat(flashblocks rpc): Full revamp of X Layer custom flashblocks RPC layer#176
sieniven wants to merge 43 commits intomainfrom
niven/cleanup-flashblocks-rpc

Conversation

@sieniven
Copy link
Contributor

@sieniven sieniven commented Mar 10, 2026

Summary

This PR revamps the entire flashblocks rpc layer design, similar to the previous Realtime-feature (RTA) cache capabilities on the since discontinued xlayer-erigon (refer to: https://github.com/okx/xlayer-erigon/tree/dev/zk/realtime).

Essentially, this revamp implements the full solution 2 mentioned in the upstream reth's issue discussion thread: paradigmxyz/reth#20491, which is a more elegant design to the complex op-reth's flashblock rpc layer that is still dependent on the canonical chainstate.

Motivations

The best design for allowing minimal latencies is to allow a full state accumulation from incoming flashblocks that are ahead of the canonical chainstate, while allowing for a hybrid sync approach that:

  1. State accumulated from incoming flashblocks that are ahead of the canonical chainstate are stored in memory
  2. Optimize default CL sync from the state accumulation through a pre-warming mechanism of already executed flashblocks sequence (skip payload re-validation entirely)
  3. Automatic flushing of flashblocks state layer once canonical chainstate catches up
  4. State queries (pending and confirmed states, also supporting latest tags) on flashblocks rpc tries to read from both the flashblocks state cache and underlying canon provider
  5. State queries handle edge cases automatically such as chain re-orgs, flashblocks re-orgs, and flashblocks state layer lagging behind canonical chainstate

New flashblocks rpc design

1. Flashblock cache layer

  • Adds the new FlashblockStateCache layer which includes both the pending state and confirm state caches
  • Confirm state cache indexes and stores full block data (state and stateless) in moemory
  • Pending state cache stores the current flashblocks sequence state received from the sequencer
  • On cache misses on the flashblocks state cache, all state queries are proxied to the underlying canon provider
  • To handle edge case where canon provider is ahead of flashblocks state cache and cache reorg has not yet been triggered, if state retrieval hits on both underlying canon provider and flashblocks cache, we prioritize canon provider as the source of truth

2. Flashblocks eth rpc extension

  • Adds the new flashblocks-eth rpc extension (and maintaining compatibility with upstream OpEthApi)
  • Override all eth json-rpc APIs to support using the new flashblocks state cache layer and the underlying canon chainstate provider

3. Revamp worker

  • Change logic to by default always calculate state root. Merklization on payload validation has been optimized greatly on reth's v1.11 release onwards with the new sparse trie implementation (feat: sparse trie as cache paradigmxyz/reth#21583) and is no longer a bottleneck here
  • Align worker logic to the default reth's payload validation logic
  • TODO: add support for flashblocks block access list for faster flashblocks sequence execution and validation

4. Remove sequence manager, revamp to RawCache

  • Removes the sequence manager entirely and its overly complicated updating logic from the execution task completion thread (updating calculated SR near target flashblocks count and re-broadcasting the same sequence)
  • Simplifying to use a raw cache, which accumulates the raw flashblocks received by the sequencer
  • Raw cache maintains similar logic to get the next best build-args, while tracking the last fully executed flashblocks sequence index

5. Add handlers for flashblocks state update logic

  • Add handle_flashblocks handler - handle receiving a raw flashblock, and updating the raw cache
  • Add handle_flashblocks_state handler - handle spawning task execution for flashblocks sequence validation and updating the flashblocks state cache
  • Add handle_canonical_block - logic to handle canonical chainstate updates to flush flashblocks state cache layer, and also handle extreme cases like chain reorgs, etc

Supported flashblocks eth APIs

Block apis
[✅] eth_blockNumber
[✅] eth_getBlockByNumber
[✅] eth_getBlockByHash
[✅] eth_getBlockReceipts
[✅] eth_getBlockTransactionCountByNumber
[✅] eth_getBlockTransactionCountByHash

Transaction apis
[✅] eth_getTransactionByHash
[✅] eth_getRawTransactionByHash
[✅] eth_getTransactionReceipt
[✅] eth_getTransactionByBlockHashAndIndex
[✅] eth_getTransactionByBlockNumberAndIndex
[✅] eth_getRawTransactionByBlockHashAndIndex
[✅] eth_getRawTransactionByBlockNumberAndIndex
[✅] eth_sendRawTransactionSync

State apis
[✅] eth_call
[✅] eth_estimateGas
[✅] eth_getBalance
[✅] eth_getTransactionCount
[✅] eth_getCode
[✅] eth_getStorageAt

TODO:
Support eth_getLogs in the future

Design flaws on op-reth flashblocks rpc

  1. The flashblocks rpc layer is tightly coupled to the canonical chainstate height. Thus, although they have since added optimistic builds to advance the pending state, however confirmed chainstates (using "latest" tags) are still dependent on the canonical chainstate
  2. Flashblocks consensus sync mode does not make sense since flashblocks finality is less probably than default blocks. Flashblocks consensus sync also cannot currently account for rollbacks
  3. Lack of support or design consideration for hybrid sync approach - tightly coupled with reason 2. Using a hybrid sync approach (CL sync + flashblocks state updates) is ideal for flashblocks design. However, full payloads from CL driven sync are currently re-executed on the default payload validator on engine_newPayload (even though the flashblocks sequence had already executed and validated the exact same payload)
  4. SR calculations are triggered based on configurable values instead of a more deterministic approach, which can be a point of failure on rpc consensus. Also with reth's sparse trie async task feature implemented on reth v1.11 onwards, incremental SR calculations on payload validation becomes much cheaper
  5. No flashblocks BAL implemented

sieniven and others added 16 commits March 9, 2026 15:49
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…to state cache

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a tertiary tx hash index (HashMap<TxHash, Arc<CachedTxInfo>>) to
ConfirmCache, enabling O(1) lookups for transaction_by_hash,
transaction_by_hash_with_meta, and receipt_by_hash from confirmed
flashblocks that are ahead of the canonical chain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add collect_cached_block_range_while helper that threads a predicate
through both the provider prefix and cached suffix, enabling
sealed_headers_while to serve confirmed flashblock headers instead
of bypassing the cache entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… rpc crate

Consolidates flashblocks cache provider trait implementations (block,
header, factory, receipt, transaction) into a single eth.rs override
module in the rpc crate. Wires FlashblockStateCache into the node's
RPC registration. Simplifies the cache layer to only manage state.

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…an up cache interfaces

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sieniven and others added 8 commits March 12, 2026 21:31
Consolidate the previously split flashblocks service setup and state
cache creation into one cohesive `if let Some` block. This fixes a
subtle bug where using `.map()` with `?` operators would produce
`Option<Result<T, E>>` instead of the intended `Option<T>`, causing
`?` to not propagate errors to the outer closure.

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… access

Restructure ConfirmCache to store ExecutedBlock directly instead of just
BlockAndReceipts, enabling state provider overlay for flashblock state queries.

- Add ConfirmedBlock struct wrapping ExecutedBlock + receipts
- Update insert/remove signatures to accept ExecutedBlock
- Make PendingSequence.tx_index pub for state access
- Fix execution cache test assertions

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cks state

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sieniven sieniven force-pushed the niven/cleanup-flashblocks-rpc branch from f8a26bf to 5d96010 Compare March 13, 2026 10:15
@sieniven sieniven force-pushed the niven/cleanup-flashblocks-rpc branch from 5d96010 to 6666f70 Compare March 13, 2026 10:19
sieniven and others added 2 commits March 13, 2026 19:41
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sieniven and others added 16 commits March 16, 2026 10:52
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ate provider helpers

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ution mod

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace associated-type-based `Eth` generic with explicit `N` (node) and
`Rpc` (converter) generics on `XLayerEthApiExt` and helper functions.
Use fully-qualified `EthApiServer::` calls instead of inherent method
dispatch for clearer trait resolution. Simplify helper function bounds
by parameterizing directly on `Rpc: RpcConvert` instead of
`Eth: EthApiTypes`. Update storage slot type to `JsonStorageKey` and
fix transaction body access to use direct field/method calls.

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename `eth.rs` to `flashblocks.rs` and `xlayer_ext.rs` to `default.rs`
to better reflect their responsibilities. Rename `XLayerEthApiExt` to
`FlashblocksEthApiExt`, `XlayerRpcExt` to `DefaultRpcExt`, and
`XlayerRpcExtApi` to `DefaultRpcExtApi`. Extract `FlashblocksRpcArgs`
as a standalone struct with derive macros and update `main.rs` to use
the new type names and module paths.

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove unused `reth-rpc` and `async-trait` dependencies from
`crates/rpc/Cargo.toml`. Remove stale comment on converter field,
fix redundant parentheses in payload builder, and use associated
`N::Primitives` type instead of concrete `OpPrimitives` in
`get_flashblock_state_provider_by_id` return type.

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant