Skip to content

feat(rpc): add transaction hash caching to EthStateCache#21180

Merged
mattsse merged 27 commits intoparadigmxyz:mainfrom
figtracer:cache-txs
Jan 26, 2026
Merged

feat(rpc): add transaction hash caching to EthStateCache#21180
mattsse merged 27 commits intoparadigmxyz:mainfrom
figtracer:cache-txs

Conversation

@figtracer
Copy link
Contributor

@figtracer figtracer commented Jan 19, 2026

Summary

Adds O(1) transaction hash lookup caching to the RPC layer's EthStateCache. This speeds up eth_getTransactionByHash and eth_getTransactionReceipt for recently cached blocks by avoiding database lookups.

Changes

  • Introduces CachedTransaction<B, R> helper struct containing:

    • block: Arc<RecoveredBlock<B>> - the cached block (shared with block cache)
    • tx_index: usize - transaction index within the block
    • receipts: Option<Arc<Vec<R>>> - optional receipts (shared with receipts cache)
  • Adds tx_hash_index: LruMap<TxHash, Rc(B256, usize)> to EthStateCacheService that indexes transaction hashes to their block hash and index

  • New get_transaction_by_hash() method returns the full CachedTransaction with block and optional receipts in a single lookup

  • Transactions are indexed when new canonical blocks arrive via CacheNewCanonicalChain and removed on reorgs via RemoveReorgedChain

  • Adds --rpc-cache.max-cached-tx-hashes CLI option (default: 30,000) to configure the transaction hash cache size

@figtracer
Copy link
Contributor Author

doesn't show results in practice

@figtracer figtracer closed this Jan 19, 2026
@github-project-automation github-project-automation bot moved this from Backlog to Done in Reth Tracker Jan 19, 2026
Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

@figtracer I like this idea, but perhaps we can solve this on the rpc level, like monitor new blocks and track the index there, see ethrpccache

then we could ptimistaically use the cached block already

@figtracer
Copy link
Contributor Author

@figtracer I like this idea, but perhaps we can solve this on the rpc level, like monitor new blocks and track the index there, see ethrpccache

then we could ptimistaically use the cached block already

thanks, will reopen and expand into this

@figtracer figtracer reopened this Jan 19, 2026
@github-project-automation github-project-automation bot moved this from Done to In Progress in Reth Tracker Jan 19, 2026
@figtracer figtracer changed the title perf(chain-state): add cache for tx hashes perf(chain-state): add tx_hash_index to EthStateCacheService Jan 19, 2026
Moving transaction hash caching to RPC layer instead, following
maintainer suggestion to use EthStateCache pattern.
@figtracer figtracer changed the title perf(chain-state): add tx_hash_index to EthStateCacheService perf(rpc): add tx_hash_index to EthStateCacheService Jan 19, 2026
Add a transaction hash index to EthStateCache that maps TxHash to
(BlockHash, TxIndex) for O(1) transaction lookups on recently cached
blocks.

Changes:
- Add tx_hash_index HashMap to EthStateCacheService
- Add block_tx_hashes HashMap to track txs per block for cleanup
- Index transactions when blocks are cached via CacheNewCanonicalChain
- Remove tx index entries when blocks are reorged via RemoveReorgedChain
- Add get_transaction_by_hash() method to EthStateCache
- Update LoadTransaction::transaction_by_hash() to check cache first
Replace HashMap with schnellru::LruMap for the transaction hash index.
This eliminates the need for block_tx_hashes tracking since the LRU
automatically evicts old entries when full.

Benefits:
- Saves ~32MB memory (no block_tx_hashes HashMap)
- Self-managing via LRU eviction
- Simpler code with same O(1) lookup performance
- Graceful degradation on cache miss (falls back to DB)
@figtracer figtracer changed the title perf(rpc): add tx_hash_index to EthStateCacheService feat(rpc): add transaction hash caching to EthStateCache Jan 19, 2026
@figtracer figtracer marked this pull request as ready for review January 19, 2026 16:34
@figtracer figtracer requested a review from mattsse January 19, 2026 16:34
Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

this makes a lot of sense actually and will be more important once we no longer have the index in mdbx directly

left some suggestions

max_receipts: 2000,
max_headers: 1000,
max_concurrent_db_requests: 512,
max_cached_tx_hashes: 20_000,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can bump this to 30k by default which should maybe be ~150MB or so

Copy link
Contributor Author

Choose a reason for hiding this comment

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

how do we get this number?

Comment on lines +28 to +37
/// Cached data for a transaction lookup.
#[derive(Debug, Clone)]
pub struct CachedTransaction<B: Block, R> {
/// The block containing this transaction.
pub block: Arc<RecoveredBlock<B>>,
/// Index of the transaction within the block.
pub tx_index: usize,
/// Receipts for the block, if available.
pub receipts: Option<Arc<Vec<R>>>,
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd be nice if this has a helper fn for creating the receipt directly

which could use:

/// Converts a transaction and its receipt into the rpc receipt format using the given converter.
pub fn convert_transaction_receipt<N, C>(
block: &RecoveredBlock<BlockTy<N>>,
all_receipts: &[ReceiptTy<N>],
tx: IndexedTx<'_, BlockTy<N>>,
receipt: &ReceiptTy<N>,
converter: &C,
) -> Option<Result<<C::Network as RpcTypes>::Receipt, C::Error>>
where
N: NodePrimitives,
C: RpcConvert<Primitives = N>,
{

so we can move this type there as well I believe

// `EthStateCacheService` task. It is created in `index_block_transactions`, accessed in the
// `GetTransactionByHash` handler, and removed in `remove_block_transactions` - all within
// the same task's poll loop.
unsafe impl<Provider, Tasks, LimitBlocks, LimitReceipts, LimitHeaders> Send
Copy link
Collaborator

Choose a reason for hiding this comment

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

instead of this, let's do a warpper helper around rc

@figtracer figtracer requested a review from gakonst as a code owner January 22, 2026 16:40
Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

very nice

@mattsse mattsse enabled auto-merge January 26, 2026 14:04
@mattsse mattsse added M-changelog This change should be included in the changelog A-rpc Related to the RPC implementation labels Jan 26, 2026
@mattsse mattsse added this pull request to the merge queue Jan 26, 2026
Merged via the queue into paradigmxyz:main with commit ab68557 Jan 26, 2026
45 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Reth Tracker Jan 26, 2026
@figtracer figtracer deleted the cache-txs branch January 26, 2026 17:17
rakita pushed a commit that referenced this pull request Jan 26, 2026
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
Co-authored-by: Amp <amp@ampcode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-rpc Related to the RPC implementation M-changelog This change should be included in the changelog

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants