Skip to content

Conversation

@prestwich
Copy link
Member

@prestwich prestwich commented Feb 11, 2026

Summary

  • Adds the signet-rpc-storage crate providing Ethereum JSON-RPC endpoints backed by signet-storage (hot + cold), independent of reth's FullNodeComponents
  • StorageRpcCtx wraps UnifiedStorage, BlockTags, system constants, optional TxCache, and RPC gas cap
  • BlockTags provides atomic latest/safe/finalized block tracking with sync/async resolution
  • Implements 24 supported ETH namespace endpoints:
    • Simple queries: blockNumber, chainId
    • Block queries: getBlockByHash/Number, getBlockTransactionCount*, getBlockReceipts, getBlockHeader*
    • Transaction queries: getTransactionByHash, getRawTransactionByHash, *ByBlockAndIndex, getTransactionReceipt
    • Account state (hot storage): getBalance, getStorageAt, getTransactionCount, getCode
    • EVM execution: call, estimateGas via signet-evm/trevm
    • Transaction submission: sendRawTransaction via TxCache
    • Logs: getLogs with bloom filter matching
  • 30 unsupported methods return explicit "not supported" errors
  • Bumps storage deps from 0.1.0 to 0.2.0 for ReceiptContext support

Test plan

  • cargo clippy -p signet-rpc-storage --all-features --all-targets — clean
  • cargo clippy -p signet-rpc-storage --no-default-features --all-targets — clean
  • cargo +nightly fmt — clean
  • cargo t -p signet-rpc-storage — all tests pass
  • cargo doc -p signet-rpc-storage — docs build successfully

🤖 Generated with Claude Code

@prestwich prestwich requested a review from a team as a code owner February 11, 2026 21:36
@prestwich prestwich changed the title feat: add signet-rpc-storage crate scaffolding feat: add signet-rpc-storage crate with ETH RPC endpoints Feb 12, 2026
@@ -0,0 +1,578 @@
//! Integration tests for the `signet-rpc-storage` ETH RPC endpoints.
Copy link
Member Author

Choose a reason for hiding this comment

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

[Claude Code]

Test coverage gap: eth_call and eth_estimateGas are the only two supported endpoints without integration tests. They require full EVM state setup (deployed contract bytecode, proper storage) to exercise meaningfully. Worth adding as a follow-up.

The current tests cover all 22 other supported endpoints plus error paths.

@prestwich
Copy link
Member Author

[Claude Code]

Review Summary

What's here

  • 24 ETH JSON-RPC endpoints (22 supported + 23 explicitly unsupported)
  • Clean crate structure: ctx, resolve, eth/{endpoints, helpers, error, mod}
  • 23 integration tests covering all supported endpoints except eth_call and eth_estimateGas
  • No TODOs, no todo!(), no unimplemented!(), no naked unwrap() in production code
  • Clippy clean on both --all-features and --no-default-features

Issues flagged (inline comments)

Severity Location Issue
High endpoints.rs:647-651 sendRawTransaction fire-and-forget — forwarding errors silently discarded
Medium endpoints.rs:720-722 getLogs range validation — reversed from > to silently accepted
Low helpers.rs:274-276 unreachable!() in build_receipt_envelope — future alloy TxType variants will panic
Info tests/eth_rpc.rs eth_call and eth_estimateGas not yet tested (EVM state setup needed)

Codebase health

  • Zero TODO/FIXME comments
  • Zero panic!/unwrap in non-test code
  • Consistent error handling pattern (.map_err(|e| e.to_string()))
  • Good use of tokio::try_join! for concurrent cold storage reads
  • Tracing spans on EVM execution paths (eth_call, eth_estimateGas)

prestwich and others added 5 commits February 12, 2026 07:51
Add the foundational scaffolding for the signet-rpc-storage crate, which
provides an Ethereum JSON-RPC server backed by signet-storage's unified
storage backend, independent of reth's FullNodeComponents.

This includes:
- Workspace dependency additions (signet-storage, signet-cold, signet-hot,
  signet-storage-types)
- StorageRpcCtx context struct with Arc<Inner> pattern
- BlockTags atomic block tag tracker for Latest/Safe/Finalized
- Block ID and block tag resolution utilities
- Stub eth module (endpoints to be added in follow-up)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement all ETH namespace JSON-RPC endpoints backed by cold/hot storage
instead of reth. Converts eth.rs placeholder into eth/ directory module
with error types, helpers, and 24 supported endpoint handlers:

- Simple queries: blockNumber, chainId
- Block queries: getBlockByHash/Number, getBlockReceipts, headers
- Transaction queries: getTransactionByHash, getTransactionReceipt, raw txs
- Account state (hot storage): getBalance, getStorageAt, getCode, getTransactionCount
- EVM execution: call, estimateGas (via signet-evm/trevm)
- Transaction submission: sendRawTransaction (via TxCache)
- Logs: getLogs with bloom filter matching

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 23 integration tests covering all endpoint categories: simple
queries, block/transaction lookups, account state, logs, and error
cases. Tests exercise the router through the axum service layer using
tower's oneshot().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- send_raw_transaction: log warning on forwarding failure instead of
  silently discarding the error
- get_logs: reject reversed block ranges (from > to) with an explicit
  error instead of silently returning empty results
- build_receipt_envelope: remove catch-all arm so new TxType variants
  from alloy produce a compile error instead of a runtime panic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Regular txs execute before system txs, not the other way around.

Drive-by from #74 (comment)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@prestwich prestwich force-pushed the feat/rpc-storage-scaffolding branch from 4341320 to 601c156 Compare February 12, 2026 12:51
) -> Result<u64, ResolveError> {
match id {
BlockId::Number(tag) => resolve_block_number_or_tag(tag, tags),
BlockId::Hash(h) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

doesn't this cause a double header lookup when calling getHeader? can we remove that?

prestwich and others added 3 commits February 12, 2026 08:07
- Extract `resolve_evm_block` method on `StorageRpcCtx` to deduplicate
  the block resolution + header fetch + revm db creation shared by
  `call()` and `estimate_gas()`. Resolves headers directly (by hash or
  by tag→number) to avoid redundant cold storage lookups.
- Replace glob import `use endpoints::*` with explicit imports.
- Remove unused `revm_state()` method from `StorageRpcCtx`.

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

- Move `resolve_block_id` and `resolve_block_number_or_tag` from free
  functions in resolve.rs to `resolve_block_id` and `resolve_block_tag`
  methods on `StorageRpcCtx`. This eliminates repeated `ctx.tags()` and
  `ctx.cold()` threading at every call site.
- `resolve_block_tag` returns `u64` directly (infallible) instead of
  `Result`, simplifying callers like `get_logs`.
- Remove `H::RoTx: Send + Sync + 'static` bounds from all endpoint
  functions, router, and ctx methods — the trait already provides these.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the bare `rpc_gas_cap` constructor parameter with a
`StorageRpcConfig` struct that bundles all RPC configuration.
This moves `max_blocks_per_filter` from a hard-coded constant to
a configurable value, adds `max_logs_per_response` enforcement
in `eth_getLogs`, and pre-creates a tracing semaphore for future
debug endpoint concurrency limiting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
let mut all_logs = Vec::new();

for (chunk_start, chunk_end) in
BlockRangeInclusiveIter::new(from..=to, MAX_HEADERS_RANGE)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm guessing the intent here is to return batches of 1000, but the way BlockRangeInclusiveIter is implemented, we'd need to pass MAX_HEADERS_RANGE - 1 as the step value.

I think it might be worth changing the BlockRangeInclusiveIter to treat the step arg the same way as the std lib - i.e. to make it mean "batch size" and not increment it by 1 internally. That would mean we'd also need to update BlockRangeInclusiveIter::next() to have let end = (start + self.step - 1).min(self.end);

Copy link
Member Author

Choose a reason for hiding this comment

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

this is basically ported from reth, so i'll take a deeper look here

// ---------------------------------------------------------------------------

pub(crate) async fn not_supported() -> ResponsePayload<(), ()> {
ResponsePayload::internal_error_message(Cow::Borrowed("Method not supported."))
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be method_not_found() rather than an internal error, so callers don't retry.

&meta,
receipt,
gas_used,
0, // log_index_offset: single receipt, no prior logs
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this offset be relative to all the logs for the block as opposed to just this tx?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes 🤮

Copy link
Member Author

Choose a reason for hiding this comment

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

gonna have to update Storage to return this info

Add gas oracle, filters, subscriptions, debug tracing, and signet
namespaces to signet-rpc-storage. Port 15 endpoints from the old
reth-backed signet-rpc crate to the storage-backed architecture.

New modules:
- gas_oracle: cold-storage gas price oracle (suggest_tip_cap)
- interest/: filter manager, subscription manager, block notifications
- debug/: traceBlockByNumber, traceBlockByHash, traceTransaction
- signet/: sendOrder, callBundle

Wired eth endpoints: gasPrice, maxPriorityFeePerGas, feeHistory,
newFilter, newBlockFilter, uninstallFilter, getFilterChanges,
getFilterLogs, subscribe, unsubscribe.

Integration tests cover gas/fee queries, filter lifecycle, and
debug tracing with noop tracer.

Co-Authored-By: Claude Opus 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.

2 participants