Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/net/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ tokio.workspace = true
ethlambda-metrics.workspace = true
tracing.workspace = true
ethlambda-storage.workspace = true
ethlambda-types.workspace = true
serde.workspace = true
serde_json.workspace = true

Expand Down
70 changes: 34 additions & 36 deletions crates/net/rpc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::net::SocketAddr;

use axum::{Json, Router, response::IntoResponse, routing::get};
use axum::{Json, Router, http::HeaderValue, http::header, response::IntoResponse, routing::get};
use ethlambda_storage::Store;
use ethlambda_types::primitives::Encode;

pub(crate) const JSON_CONTENT_TYPE: &str = "application/json; charset=utf-8";
pub(crate) const SSZ_CONTENT_TYPE: &str = "application/octet-stream";

pub mod metrics;

Expand Down Expand Up @@ -35,14 +39,32 @@ async fn get_latest_finalized_state(
let state = store
.get_state(&finalized.root)
.expect("finalized state exists");
Json(state)
ssz_response(state.as_ssz_bytes())
}

async fn get_latest_justified_state(
axum::extract::State(store): axum::extract::State<Store>,
) -> impl IntoResponse {
let checkpoint = store.latest_justified();
Json(checkpoint)
json_response(checkpoint)
}

fn json_response<T: serde::Serialize>(value: T) -> axum::response::Response {
let mut response = Json(value).into_response();
response.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static(JSON_CONTENT_TYPE),
);
response
}

fn ssz_response(bytes: Vec<u8>) -> axum::response::Response {
let mut response = bytes.into_response();
response.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static(SSZ_CONTENT_TYPE),
);
response
}

#[cfg(test)]
Expand Down Expand Up @@ -126,12 +148,15 @@ mod tests {

#[tokio::test]
async fn test_get_latest_finalized_state() {
use ethlambda_types::primitives::Encode;

let state = create_test_state();
let store = Store::from_genesis(state);

// Get the expected state from the store to build expected JSON
// Get the expected state from the store
let finalized = store.latest_finalized();
let expected_state = store.get_state(&finalized.root).unwrap();
let expected_ssz = expected_state.as_ssz_bytes();

let app = build_api_router(store);

Expand All @@ -146,39 +171,12 @@ mod tests {
.unwrap();

assert_eq!(response.status(), StatusCode::OK);

let body = response.into_body().collect().await.unwrap().to_bytes();
let returned_state: serde_json::Value = serde_json::from_slice(&body).unwrap();

let header = &expected_state.latest_block_header;
assert_eq!(
returned_state,
json!({
"config": {
"genesis_time": expected_state.config.genesis_time
},
"slot": expected_state.slot,
"latest_block_header": {
"slot": header.slot,
"proposer_index": header.proposer_index,
"parent_root": format!("{:#x}", header.parent_root),
"state_root": format!("{:#x}", header.state_root),
"body_root": format!("{:#x}", header.body_root)
},
"latest_justified": {
"slot": expected_state.latest_justified.slot,
"root": format!("{:#x}", expected_state.latest_justified.root)
},
"latest_finalized": {
"slot": expected_state.latest_finalized.slot,
"root": format!("{:#x}", expected_state.latest_finalized.root)
},
"historical_block_hashes": [],
"justified_slots": "0x01",
"validators": [],
"justifications_roots": [],
"justifications_validators": "0x01"
})
response.headers().get(header::CONTENT_TYPE).unwrap(),
SSZ_CONTENT_TYPE
);

let body = response.into_body().collect().await.unwrap().to_bytes();
assert_eq!(body.as_ref(), expected_ssz.as_slice());
}
}
13 changes: 10 additions & 3 deletions crates/net/rpc/src/metrics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use axum::{Router, http::HeaderValue, response::IntoResponse, routing::get};
use axum::{Router, http::HeaderValue, http::header, response::IntoResponse, routing::get};
use ethlambda_metrics::gather_default_metrics;
use tracing::warn;

Expand All @@ -9,7 +9,12 @@ pub fn start_prometheus_metrics_api() -> Router {
}

pub(crate) async fn get_health() -> impl IntoResponse {
r#"{"status": "healthy", "service": "lean-spec-api"}"#
let mut response = r#"{"status":"healthy","service":"lean-spec-api"}"#.into_response();
response.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static(crate::JSON_CONTENT_TYPE),
);
response
}

pub(crate) async fn get_metrics() -> impl IntoResponse {
Expand All @@ -20,6 +25,8 @@ pub(crate) async fn get_metrics() -> impl IntoResponse {
.unwrap_or_default()
.into_response();
let content_type = HeaderValue::from_static("text/plain; version=0.0.4; charset=utf-8");
response.headers_mut().insert("content-type", content_type);
response
.headers_mut()
.insert(header::CONTENT_TYPE, content_type);
response
}