From ad47dcf228ce6c7e92485be31f31b04d4c2649a5 Mon Sep 17 00:00:00 2001 From: Al Liu Date: Wed, 31 Jan 2024 16:30:48 +0800 Subject: [PATCH] Block explorer 2 (#566) * support query block list from explorer --- nodes/explorer/Cargo.toml | 6 +- nodes/explorer/src/api_backend/mod.rs | 7 +- nodes/explorer/src/api_backend/store.rs | 185 +++++++++++++++++++- nomos-services/storage/src/backends/sled.rs | 1 - nomos-services/storage/src/lib.rs | 8 +- 5 files changed, 197 insertions(+), 10 deletions(-) diff --git a/nodes/explorer/Cargo.toml b/nodes/explorer/Cargo.toml index 06cee0cd..a1eefda2 100644 --- a/nodes/explorer/Cargo.toml +++ b/nodes/explorer/Cargo.toml @@ -12,6 +12,7 @@ nomos-storage = { path = "../../nomos-services/storage", features = ["sled"] } nomos-api = { path = "../../nomos-services/api" } nomos-node = { path = "../nomos-node" } nomos-core = { path = "../../nomos-core" } +full-replication = { path = "../../nomos-da/full-replication" } serde = "1.0" tracing = "0.1" async-trait = "0.1.73" @@ -19,8 +20,9 @@ axum = "0.6" hyper = "0.14.27" tower-http = "0.4" utoipa = "4.0" -utoipa-swagger-ui = "4.0" +utoipa-swagger-ui = "6.0" futures = "0.3" serde_yaml = "0.9.25" clap = { version = "4.4.6", features = ["derive"] } -eyre = "0.6" \ No newline at end of file +eyre = "0.6" + diff --git a/nodes/explorer/src/api_backend/mod.rs b/nodes/explorer/src/api_backend/mod.rs index 199576b3..0a384e5d 100644 --- a/nodes/explorer/src/api_backend/mod.rs +++ b/nodes/explorer/src/api_backend/mod.rs @@ -84,7 +84,12 @@ where ) .layer(TraceLayer::new_for_http()) // .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi())) - .route("/blocks", routing::get(store::store_blocks::)) + .route("/store/blocks", routing::get(store::store_blocks::)) + .route("/explorer/blocks", routing::get(store::blocks::)) + .route( + "/explorer/blocks/depth", + routing::get(store::block_depth::), + ) .with_state(handle); Server::bind(&self.settings.address) diff --git a/nodes/explorer/src/api_backend/store.rs b/nodes/explorer/src/api_backend/store.rs index 395cbd70..de2cbd1b 100644 --- a/nodes/explorer/src/api_backend/store.rs +++ b/nodes/explorer/src/api_backend/store.rs @@ -4,12 +4,14 @@ use std::hash::Hash; // crates use axum::extract::{Query, State}; -use axum::response::Response; +use axum::response::{IntoResponse, Response}; +use hyper::StatusCode; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; // internal +use full_replication::Certificate; use nomos_api::http::storage; -use nomos_core::block::BlockId; +use nomos_core::block::{Block, BlockId}; use nomos_core::tx::Transaction; use nomos_node::make_request_and_return_response; use nomos_storage::backends::StorageSerde; @@ -44,3 +46,182 @@ where .collect(); make_request_and_return_response!(futures::future::try_join_all(results)) } + +#[derive(Deserialize)] +pub(crate) struct BlocksByIdQueryParams { + from: BlockId, + to: Option, +} + +pub(crate) async fn blocks( + State(store): State, + Query(query): Query, +) -> Response +where + Tx: Transaction + + Clone + + Debug + + Eq + + Hash + + Serialize + + DeserializeOwned + + Send + + Sync + + 'static, + ::Hash: std::cmp::Ord + Debug + Send + Sync + 'static, + S: StorageSerde + Send + Sync + 'static, +{ + let BlocksByIdQueryParams { from, to } = query; + // get the from block + let from = match storage::block_req::(&store, from).await { + Ok(from) => match from { + Some(from) => from, + None => { + return IntoResponse::into_response((StatusCode::NOT_FOUND, "from block not found")) + } + }, + Err(e) => { + return IntoResponse::into_response((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())) + } + }; + + // check if to is valid + match to { + Some(to) => match storage::block_req::(&store, to).await { + Ok(to) => match to { + Some(to) => handle_to::(store, from, Some(to)).await, + None => IntoResponse::into_response((StatusCode::NOT_FOUND, "to block not found")), + }, + Err(e) => { + IntoResponse::into_response((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())) + } + }, + None => handle_to::(store, from, None).await, + } +} + +async fn handle_to( + store: OverwatchHandle, + from: Block, + to: Option>, +) -> Response +where + Tx: Transaction + + Clone + + Debug + + Eq + + Hash + + Serialize + + DeserializeOwned + + Send + + Sync + + 'static, + ::Hash: std::cmp::Ord + Debug + Send + Sync + 'static, + S: StorageSerde + Send + Sync + 'static, +{ + let mut current = Some(from.header().parent()); + let mut blocks = Vec::new(); + while let Some(id) = current { + if let Some(to) = &to { + if id == to.header().id { + break; + } + } + + let block = match storage::block_req::(&store, id).await { + Ok(block) => block, + Err(e) => { + return IntoResponse::into_response(( + StatusCode::INTERNAL_SERVER_ERROR, + e.to_string(), + )) + } + }; + + match block { + Some(block) => { + current = Some(block.header().parent()); + blocks.push(block); + } + None => { + current = None; + } + } + } + + IntoResponse::into_response((StatusCode::OK, ::axum::Json(blocks))) +} + +#[derive(Deserialize)] +pub(crate) struct BlocksByDepthQueryParams { + from: BlockId, + #[serde(default = "default_depth")] + depth: usize, +} + +fn default_depth() -> usize { + 500 +} + +pub(crate) async fn block_depth( + State(store): State, + Query(query): Query, +) -> Response +where + Tx: Transaction + + Clone + + Debug + + Eq + + Hash + + Serialize + + DeserializeOwned + + Send + + Sync + + 'static, + ::Hash: std::cmp::Ord + Debug + Send + Sync + 'static, + S: StorageSerde + Send + Sync + 'static, +{ + let BlocksByDepthQueryParams { from, depth } = query; + // get the from block + let from = match storage::block_req::(&store, from).await { + Ok(from) => match from { + Some(from) => from, + None => { + return IntoResponse::into_response((StatusCode::NOT_FOUND, "from block not found")) + } + }, + Err(e) => { + return IntoResponse::into_response((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())) + } + }; + + let mut current = Some(from.header().parent()); + let mut blocks = Vec::new(); + while blocks.len() < depth { + if let Some(id) = current { + let block = match storage::block_req::(&store, id).await { + Ok(block) => block, + Err(e) => { + return IntoResponse::into_response(( + StatusCode::INTERNAL_SERVER_ERROR, + e.to_string(), + )) + } + }; + + match block { + Some(block) => { + current = Some(block.header().parent()); + blocks.push(block); + } + None => { + current = None; + } + } + } else { + break; + } + } + + IntoResponse::into_response((StatusCode::OK, ::axum::Json(blocks))) +} diff --git a/nomos-services/storage/src/backends/sled.rs b/nomos-services/storage/src/backends/sled.rs index 0afba591..5783bd21 100644 --- a/nomos-services/storage/src/backends/sled.rs +++ b/nomos-services/storage/src/backends/sled.rs @@ -40,7 +40,6 @@ impl StorageTransaction for SledTransaction { } /// Sled storage backend - pub struct SledBackend { sled: sled::Db, _serde_op: PhantomData, diff --git a/nomos-services/storage/src/lib.rs b/nomos-services/storage/src/lib.rs index b2754f03..35c57e0c 100644 --- a/nomos-services/storage/src/lib.rs +++ b/nomos-services/storage/src/lib.rs @@ -136,16 +136,16 @@ impl StorageMsg { impl Debug for StorageMsg { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - StorageMsg::Load { key, .. } => { + Self::Load { key, .. } => { write!(f, "Load {{ {key:?} }}") } - StorageMsg::Store { key, value } => { + Self::Store { key, value } => { write!(f, "Store {{ {key:?}, {value:?}}}") } - StorageMsg::Remove { key, .. } => { + Self::Remove { key, .. } => { write!(f, "Remove {{ {key:?} }}") } - StorageMsg::Execute { .. } => write!(f, "Execute transaction"), + Self::Execute { .. } => write!(f, "Execute transaction"), } } }