refactor(rpc): compose api router from per-feature route modules#454
Conversation
Split the monolithic lib.rs API router into focused modules: core.rs holds the finalized-state/finalized-block/justified-checkpoint handlers and shared response helpers; blocks.rs, fork_choice.rs, and admin.rs each expose pub(crate) routes() -> Router<Store>. build_api_router merges them with .with_state(store). No behavior change.
🤖 Kimi Code ReviewOverall Assessment: Good refactoring that improves code organization by decentralizing route definitions into submodule Critical Issues
Moderate Issues
Minor Observations
Security Note Consensus Correctness Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Codex Code ReviewFindings
I didn’t find other correctness, security, performance, or consensus-path regressions in this PR. The change is effectively a route/module refactor; fork choice, attestation validation, justification/finalization, STF, XMSS, and SSZ logic are untouched. I couldn’t run Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Greptile SummaryThis PR splits the RPC crate's monolithic router in
Confidence Score: 4/5Safe to merge — all endpoints, paths, and response shapes are unchanged; the refactor is purely structural. The change is a clean mechanical extraction: routes move into dedicated modules, handlers become crate-private, and the merge composition preserves axum's static-before-dynamic matching priority. Two minor rough edges remain — the new module is named No files require special attention beyond the two style notes in
|
| Filename | Overview |
|---|---|
| crates/net/rpc/src/lib.rs | Monolithic router replaced with .merge() of per-feature route modules; naming a local module core shadows Rust's built-in core crate. |
| crates/net/rpc/src/core.rs | New module housing finalized-state, finalized-block, and justified-checkpoint endpoints plus shared helpers; stale comment claims genesis returns 404 but tests assert 200. |
| crates/net/rpc/src/admin.rs | Added routes() factory function; handlers tightened to pub(crate); no behavioural change. |
| crates/net/rpc/src/blocks.rs | Added routes() factory function; handlers tightened to pub(crate); no behavioural change. |
| crates/net/rpc/src/fork_choice.rs | Added routes() factory function; test helper updated to reuse it; handlers tightened to pub(crate); no behavioural change. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["start_rpc_server()"] --> B["build_api_router(store)"]
B --> C["core::routes()"]
B --> D["blocks::routes()"]
B --> E["fork_choice::routes()"]
B --> F["admin::routes()"]
C --> C1["/lean/v0/health"]
C --> C2["/lean/v0/states/finalized"]
C --> C3["/lean/v0/blocks/finalized"]
C --> C4["/lean/v0/checkpoints/justified"]
D --> D1["/lean/v0/blocks/{block_id}"]
D --> D2["/lean/v0/blocks/{block_id}/header"]
E --> E1["/lean/v0/fork_choice"]
E --> E2["/lean/v0/fork_choice/ui"]
F --> F1["/lean/v0/admin/aggregator (GET+POST)"]
A --> G["build_debug_router()"]
G --> G1["/debug/pprof/allocs"]
G --> G2["/debug/pprof/allocs/flamegraph"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["start_rpc_server()"] --> B["build_api_router(store)"]
B --> C["core::routes()"]
B --> D["blocks::routes()"]
B --> E["fork_choice::routes()"]
B --> F["admin::routes()"]
C --> C1["/lean/v0/health"]
C --> C2["/lean/v0/states/finalized"]
C --> C3["/lean/v0/blocks/finalized"]
C --> C4["/lean/v0/checkpoints/justified"]
D --> D1["/lean/v0/blocks/{block_id}"]
D --> D2["/lean/v0/blocks/{block_id}/header"]
E --> E1["/lean/v0/fork_choice"]
E --> E2["/lean/v0/fork_choice/ui"]
F --> F1["/lean/v0/admin/aggregator (GET+POST)"]
A --> G["build_debug_router()"]
G --> G1["/debug/pprof/allocs"]
G --> G2["/debug/pprof/allocs/flamegraph"]
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
crates/net/rpc/src/core.rs:43
**Stale inline comment contradicts test behaviour**
The comment says "Returns 404 for genesis since it doesn't have a valid signature", but the integration test `test_get_latest_finalized_block_serves_genesis_with_placeholder_proof` (in `lib.rs`) explicitly documents and asserts that genesis returns 200 with a synthesised placeholder proof. The comment was copied from the original `lib.rs` without being updated, so it now misleads anyone reading this handler.
### Issue 2 of 2
crates/net/rpc/src/lib.rs:13
**Module name `core` shadows the standard library crate**
Declaring `mod core;` in a Rust crate root shadows the built-in `core` crate for all code in `lib.rs`. Any future use of `core::` within this file will resolve to the local module rather than the standard library, which can cause confusing compilation errors or subtle misbehaviour. A name like `endpoints_core`, `base`, or `common` would sidestep this shadowing entirely.
Reviews (1): Last reviewed commit: "refactor(rpc): compose api router from p..." | Re-trigger Greptile
| axum::extract::State(store): axum::extract::State<Store>, | ||
| ) -> impl IntoResponse { | ||
| let finalized = store.latest_finalized(); | ||
| // Returns 404 for genesis since it doesn't have a valid signature |
There was a problem hiding this comment.
Stale inline comment contradicts test behaviour
The comment says "Returns 404 for genesis since it doesn't have a valid signature", but the integration test test_get_latest_finalized_block_serves_genesis_with_placeholder_proof (in lib.rs) explicitly documents and asserts that genesis returns 200 with a synthesised placeholder proof. The comment was copied from the original lib.rs without being updated, so it now misleads anyone reading this handler.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/net/rpc/src/core.rs
Line: 43
Comment:
**Stale inline comment contradicts test behaviour**
The comment says "Returns 404 for genesis since it doesn't have a valid signature", but the integration test `test_get_latest_finalized_block_serves_genesis_with_placeholder_proof` (in `lib.rs`) explicitly documents and asserts that genesis returns 200 with a synthesised placeholder proof. The comment was copied from the original `lib.rs` without being updated, so it now misleads anyone reading this handler.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
|
||
| mod admin; | ||
| mod blocks; | ||
| mod core; |
There was a problem hiding this comment.
Module name
core shadows the standard library crate
Declaring mod core; in a Rust crate root shadows the built-in core crate for all code in lib.rs. Any future use of core:: within this file will resolve to the local module rather than the standard library, which can cause confusing compilation errors or subtle misbehaviour. A name like endpoints_core, base, or common would sidestep this shadowing entirely.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/net/rpc/src/lib.rs
Line: 13
Comment:
**Module name `core` shadows the standard library crate**
Declaring `mod core;` in a Rust crate root shadows the built-in `core` crate for all code in `lib.rs`. Any future use of `core::` within this file will resolve to the local module rather than the standard library, which can cause confusing compilation errors or subtle misbehaviour. A name like `endpoints_core`, `base`, or `common` would sidestep this shadowing entirely.
How can I resolve this? If you propose a fix, please make it concise.
🤖 Claude Code ReviewI have enough to write the review. Here are my findings: PR Review:
|
…eckpoint handler name - Rename `mod core` → `mod base` to avoid shadowing Rust's built-in `core` crate - Fix stale comment on `get_latest_finalized_block`: genesis returns 200 with a synthesized blank proof, not 404 - Rename handler `get_latest_justified_state` → `get_latest_justified_checkpoint` to reflect that it returns a Checkpoint, not a State
Behavior-preserving refactor of the RPC crate's router setup. Instead of a single monolithic router, routes are now composed from per-feature modules, each responsible for registering its own handlers.
This scaffolding is a prerequisite for the stacked endpoint PRs that follow: each new endpoint is added as its own module without touching unrelated code.
Passes clippy with
-D warningsand all existing tests.