diff --git a/api/src/handlers.rs b/api/src/handlers.rs index 14ec832b2..97e86e46f 100644 --- a/api/src/handlers.rs +++ b/api/src/handlers.rs @@ -62,11 +62,13 @@ pub fn start_rest_apis( chain: Arc, tx_pool: Arc>, peers: Arc, + sync_state: Arc, api_secret: Option, tls_config: Option, ) -> bool { let mut apis = ApiServer::new(); - let mut router = build_router(chain, tx_pool, peers).expect("unable to build API router"); + let mut router = + build_router(chain, tx_pool, peers, sync_state).expect("unable to build API router"); if let Some(api_secret) = api_secret { let api_basic_auth = format!("Basic {}", util::to_base64(&format!("grin:{}", api_secret))); let basic_auth_middleware = Arc::new(BasicAuthMiddleware::new( @@ -93,6 +95,7 @@ pub fn build_router( chain: Arc, tx_pool: Arc>, peers: Arc, + sync_state: Arc, ) -> Result { let route_list = vec![ "get blocks".to_string(), @@ -144,6 +147,7 @@ pub fn build_router( let status_handler = StatusHandler { chain: Arc::downgrade(&chain), peers: Arc::downgrade(&peers), + sync_state: Arc::downgrade(&sync_state), }; let kernel_download_handler = KernelDownloadHandler { peers: Arc::downgrade(&peers), diff --git a/api/src/handlers/server_api.rs b/api/src/handlers/server_api.rs index d7dbd6220..cfa144510 100644 --- a/api/src/handlers/server_api.rs +++ b/api/src/handlers/server_api.rs @@ -13,13 +13,14 @@ // limitations under the License. use super::utils::w; -use crate::chain; +use crate::chain::{Chain, SyncState, SyncStatus}; use crate::p2p; use crate::rest::*; use crate::router::{Handler, ResponseFuture}; use crate::types::*; use crate::web::*; use hyper::{Body, Request, StatusCode}; +use serde_json::json; use std::sync::Weak; // RESTful index of available api endpoints @@ -62,8 +63,9 @@ impl Handler for KernelDownloadHandler { /// Status handler. Post a summary of the server status /// GET /v1/status pub struct StatusHandler { - pub chain: Weak, + pub chain: Weak, pub peers: Weak, + pub sync_state: Weak, } impl StatusHandler { @@ -71,9 +73,13 @@ impl StatusHandler { let head = w(&self.chain)? .head() .map_err(|e| ErrorKind::Internal(format!("can't get head: {}", e)))?; + let sync_status = w(&self.sync_state)?.status(); + let (api_sync_status, api_sync_info) = sync_status_to_api(sync_status); Ok(Status::from_tip_and_peers( head, w(&self.peers)?.peer_count(), + api_sync_status, + api_sync_info, )) } } @@ -83,3 +89,50 @@ impl Handler for StatusHandler { result_to_response(self.get_status()) } } + +/// Convert a SyncStatus in a readable API representation +fn sync_status_to_api(sync_status: SyncStatus) -> (String, Option) { + match sync_status { + SyncStatus::NoSync => ("no_sync".to_string(), None), + SyncStatus::AwaitingPeers(_) => ("awaiting_peers".to_string(), None), + SyncStatus::HeaderSync { + current_height, + highest_height, + } => ( + "header_sync".to_string(), + Some(json!({ "current_height": current_height, "highest_height": highest_height })), + ), + SyncStatus::TxHashsetDownload { + start_time: _, + prev_update_time: _, + update_time: _, + prev_downloaded_size: _, + downloaded_size, + total_size, + } => ( + "txhashset_download".to_string(), + Some(json!({ "downloaded_size": downloaded_size, "total_size": total_size })), + ), + SyncStatus::TxHashsetValidation { + kernels, + kernel_total, + rproofs, + rproof_total, + } => ( + "txhashset_validation".to_string(), + Some( + json!({ "kernels": kernels, "kernel_total": kernel_total ,"rproofs": rproofs, "rproof_total": rproof_total }), + ), + ), + SyncStatus::BodySync { + current_height, + highest_height, + } => ( + "body_sync".to_string(), + Some(json!({ "current_height": current_height, "highest_height": highest_height })), + ), + SyncStatus::Shutdown => ("shutdown".to_string(), None), + // any other status is considered syncing (should be unreachable) + _ => ("syncing".to_string(), None), + } +} diff --git a/api/src/types.rs b/api/src/types.rs index 64e95d1cc..aa984e20c 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -79,15 +79,27 @@ pub struct Status { pub connections: u32, // The state of the current fork Tip pub tip: Tip, + // The current sync status + pub sync_status: String, + // Additional sync information + #[serde(skip_serializing_if = "Option::is_none")] + pub sync_info: Option, } impl Status { - pub fn from_tip_and_peers(current_tip: chain::Tip, connections: u32) -> Status { + pub fn from_tip_and_peers( + current_tip: chain::Tip, + connections: u32, + sync_status: String, + sync_info: Option, + ) -> Status { Status { protocol_version: ser::ProtocolVersion::local().into(), user_agent: p2p::msg::USER_AGENT.to_string(), connections: connections, tip: Tip::from_tip(current_tip), + sync_status, + sync_info, } } } diff --git a/chain/src/types.rs b/chain/src/types.rs index c17f6ada7..3b376eae6 100644 --- a/chain/src/types.rs +++ b/chain/src/types.rs @@ -39,7 +39,7 @@ bitflags! { } /// Various status sync can be in, whether it's fast sync or archival. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize)] #[allow(missing_docs)] pub enum SyncStatus { /// Initial State (we do not yet know if we are/should be syncing) diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index 46319e23c..1b1c5a927 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -290,6 +290,7 @@ impl Server { shared_chain.clone(), tx_pool.clone(), p2p_server.peers.clone(), + sync_state.clone(), api_secret, tls_conf, );