From 4af7780c3ef9d697a6640c8fe5d87fc811e522a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:49:50 -0300 Subject: [PATCH 1/2] feat(rpc): add GET /lean/v0/config/spec --- crates/net/rpc/src/lib.rs | 2 + crates/net/rpc/src/spec.rs | 77 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 crates/net/rpc/src/spec.rs diff --git a/crates/net/rpc/src/lib.rs b/crates/net/rpc/src/lib.rs index 09268765..5e3907a0 100644 --- a/crates/net/rpc/src/lib.rs +++ b/crates/net/rpc/src/lib.rs @@ -14,6 +14,7 @@ mod blocks; mod fork_choice; mod heap_profiling; pub mod metrics; +mod spec; pub mod test_driver; pub(crate) use base::json_response; @@ -100,6 +101,7 @@ fn build_api_router(store: Store) -> Router { .merge(blocks::routes()) .merge(fork_choice::routes()) .merge(admin::routes()) + .merge(spec::routes()) .with_state(store) } diff --git a/crates/net/rpc/src/spec.rs b/crates/net/rpc/src/spec.rs new file mode 100644 index 00000000..5a5ec0b9 --- /dev/null +++ b/crates/net/rpc/src/spec.rs @@ -0,0 +1,77 @@ +use axum::{Router, response::IntoResponse, routing::get}; +use ethlambda_blockchain::{INTERVALS_PER_SLOT, MILLISECONDS_PER_INTERVAL, MILLISECONDS_PER_SLOT}; +use ethlambda_storage::Store; +use ethlambda_types::state::HISTORICAL_ROOTS_LIMIT; +use serde::Serialize; + +use crate::json_response; + +// Dummy fork digest; keep in sync with ethlambda_p2p::gossipsub::messages::FORK_DIGEST until it's centralized in a shared crate. +const FORK_DIGEST: &str = "12345678"; + +#[derive(Serialize)] +struct SpecResponse { + #[serde(rename = "MILLISECONDS_PER_SLOT")] + ms_per_slot: u64, + #[serde(rename = "INTERVALS_PER_SLOT")] + intervals_per_slot: u64, + #[serde(rename = "MILLISECONDS_PER_INTERVAL")] + ms_per_interval: u64, + #[serde(rename = "HISTORICAL_ROOTS_LIMIT")] + historical_roots_limit: u64, + #[serde(rename = "FORK_DIGEST")] + fork_digest: String, +} + +async fn get_spec() -> impl IntoResponse { + json_response(SpecResponse { + ms_per_slot: MILLISECONDS_PER_SLOT, + intervals_per_slot: INTERVALS_PER_SLOT, + ms_per_interval: MILLISECONDS_PER_INTERVAL, + historical_roots_limit: HISTORICAL_ROOTS_LIMIT as u64, + fork_digest: FORK_DIGEST.to_string(), + }) +} + +pub(crate) fn routes() -> Router { + Router::new().route("/lean/v0/config/spec", get(get_spec)) +} + +#[cfg(test)] +mod tests { + use super::FORK_DIGEST; + use crate::test_utils::create_test_state; + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use ethlambda_blockchain::{ + INTERVALS_PER_SLOT, MILLISECONDS_PER_INTERVAL, MILLISECONDS_PER_SLOT, + }; + use ethlambda_storage::{Store, backend::InMemoryBackend}; + use http_body_util::BodyExt; + use std::sync::Arc; + use tower::ServiceExt; + + #[tokio::test] + async fn spec_returns_lean_constants() { + let store = Store::from_anchor_state(Arc::new(InMemoryBackend::new()), create_test_state()); + let app = crate::build_api_router(store); + let resp = app + .oneshot( + Request::builder() + .uri("/lean/v0/config/spec") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let body = resp.into_body().collect().await.unwrap().to_bytes(); + let json: serde_json::Value = serde_json::from_slice(&body).unwrap(); + assert_eq!(json["MILLISECONDS_PER_SLOT"], MILLISECONDS_PER_SLOT); + assert_eq!(json["INTERVALS_PER_SLOT"], INTERVALS_PER_SLOT); + assert_eq!(json["MILLISECONDS_PER_INTERVAL"], MILLISECONDS_PER_INTERVAL); + assert_eq!(json["FORK_DIGEST"], FORK_DIGEST); + } +} From 72216912320df30d4f9c15ea9cd3e8237f62c494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:38:06 -0300 Subject: [PATCH 2/2] fix(rpc): address review feedback on config/spec (test assertion, static fork_digest) --- crates/net/rpc/src/spec.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/net/rpc/src/spec.rs b/crates/net/rpc/src/spec.rs index 5a5ec0b9..27fb4cb1 100644 --- a/crates/net/rpc/src/spec.rs +++ b/crates/net/rpc/src/spec.rs @@ -20,7 +20,7 @@ struct SpecResponse { #[serde(rename = "HISTORICAL_ROOTS_LIMIT")] historical_roots_limit: u64, #[serde(rename = "FORK_DIGEST")] - fork_digest: String, + fork_digest: &'static str, } async fn get_spec() -> impl IntoResponse { @@ -29,7 +29,7 @@ async fn get_spec() -> impl IntoResponse { intervals_per_slot: INTERVALS_PER_SLOT, ms_per_interval: MILLISECONDS_PER_INTERVAL, historical_roots_limit: HISTORICAL_ROOTS_LIMIT as u64, - fork_digest: FORK_DIGEST.to_string(), + fork_digest: FORK_DIGEST, }) } @@ -49,6 +49,7 @@ mod tests { INTERVALS_PER_SLOT, MILLISECONDS_PER_INTERVAL, MILLISECONDS_PER_SLOT, }; use ethlambda_storage::{Store, backend::InMemoryBackend}; + use ethlambda_types::state::HISTORICAL_ROOTS_LIMIT; use http_body_util::BodyExt; use std::sync::Arc; use tower::ServiceExt; @@ -72,6 +73,10 @@ mod tests { assert_eq!(json["MILLISECONDS_PER_SLOT"], MILLISECONDS_PER_SLOT); assert_eq!(json["INTERVALS_PER_SLOT"], INTERVALS_PER_SLOT); assert_eq!(json["MILLISECONDS_PER_INTERVAL"], MILLISECONDS_PER_INTERVAL); + assert_eq!( + json["HISTORICAL_ROOTS_LIMIT"], + HISTORICAL_ROOTS_LIMIT as u64 + ); assert_eq!(json["FORK_DIGEST"], FORK_DIGEST); } }