diff --git a/crates/net/rpc/src/genesis.rs b/crates/net/rpc/src/genesis.rs new file mode 100644 index 00000000..45638360 --- /dev/null +++ b/crates/net/rpc/src/genesis.rs @@ -0,0 +1,69 @@ +use axum::{Router, extract::State, response::IntoResponse, routing::get}; +use ethlambda_storage::Store; +use serde::Serialize; + +use crate::json_response; + +#[derive(Serialize)] +struct GenesisResponse { + genesis_time: u64, + validator_count: u64, +} + +async fn get_genesis(State(store): State) -> impl IntoResponse { + let genesis_time = store.config().genesis_time; + // Lean validators are fixed at genesis (no churn), so the current head + // state's validator registry always equals the genesis validator count. + let validator_count = store.head_state().validators.len() as u64; + json_response(GenesisResponse { + genesis_time, + validator_count, + }) +} + +pub(crate) fn routes() -> Router { + Router::new().route("/lean/v0/genesis", get(get_genesis)) +} + +#[cfg(test)] +mod tests { + use super::*; + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use ethlambda_storage::{Store, backend::InMemoryBackend}; + use ethlambda_types::state::{State, Validator}; + use http_body_util::BodyExt; + use std::sync::Arc; + use tower::ServiceExt; + + #[tokio::test] + async fn genesis_returns_time_and_validator_count() { + // Build a state with 3 validators so the assertion is non-vacuous. + let dummy_validator = |index: u64| Validator { + attestation_pubkey: [0u8; 52], + proposal_pubkey: [0u8; 52], + index, + }; + let validators = vec![dummy_validator(0), dummy_validator(1), dummy_validator(2)]; + let state = State::from_genesis(1000, validators); + + let store = Store::from_anchor_state(Arc::new(InMemoryBackend::new()), state); + let app = routes().with_state(store); + let resp = app + .oneshot( + Request::builder() + .uri("/lean/v0/genesis") + .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["genesis_time"], 1000); + assert_eq!(json["validator_count"], 3); + } +} diff --git a/crates/net/rpc/src/lib.rs b/crates/net/rpc/src/lib.rs index 09268765..a24fc102 100644 --- a/crates/net/rpc/src/lib.rs +++ b/crates/net/rpc/src/lib.rs @@ -12,6 +12,7 @@ mod admin; mod base; mod blocks; mod fork_choice; +mod genesis; mod heap_profiling; pub mod metrics; pub mod test_driver; @@ -100,6 +101,7 @@ fn build_api_router(store: Store) -> Router { .merge(blocks::routes()) .merge(fork_choice::routes()) .merge(admin::routes()) + .merge(genesis::routes()) .with_state(store) }