// Copyright 2021 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use super::utils::w; 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}; use serde_json::json; use std::convert::TryInto; use std::sync::Weak; // RESTful index of available api endpoints // GET /v1/ pub struct IndexHandler { pub list: Vec, } impl IndexHandler {} impl Handler for IndexHandler { fn get(&self, _req: Request) -> ResponseFuture { json_response_pretty(&self.list) } } /// Status handler. Post a summary of the server status /// GET /v1/status pub struct StatusHandler { pub chain: Weak, pub peers: Weak, pub sync_state: Weak, } impl StatusHandler { pub fn get_status(&self) -> Result { let head = w(&self.chain)? .head() .map_err(|e| Error::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)? .iter() .connected() .count() .try_into() .unwrap(), api_sync_status, api_sync_info, )) } } impl Handler for StatusHandler { fn get(&self, _req: Request) -> ResponseFuture { 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::Initial => ("initial".to_string(), None), SyncStatus::NoSync => ("no_sync".to_string(), None), SyncStatus::AwaitingPeers(_) => ("awaiting_peers".to_string(), None), SyncStatus::HeaderSync { sync_head, highest_height, .. } => ( "header_sync".to_string(), Some(json!({ "current_height": sync_head.height, "highest_height": highest_height })), ), SyncStatus::TxHashsetPibd { aborted, errored, completed_leaves, leaves_required, completed_to_height, required_height, } => ( "txhashsetpibd_download".to_string(), Some(json!({ "aborted": aborted, "errored": errored, "completed_leaves":completed_leaves, "leaves_required":leaves_required, "completed_to_height": completed_to_height, "required_height":required_height})), ), SyncStatus::TxHashsetDownload(stats) => ( "txhashset_download".to_string(), Some( json!({ "downloaded_size": stats.downloaded_size, "total_size": stats.total_size }), ), ), SyncStatus::TxHashsetSetup { headers, headers_total, kernel_pos, kernel_pos_total, } => ( "txhashset_setup".to_string(), Some(json!({ "headers": headers, "headers_total": headers_total, "kernel_pos":kernel_pos, "kernel_pos_total":kernel_pos_total})), ), SyncStatus::TxHashsetRangeProofsValidation { rproofs, rproofs_total, } => ( "txhashset_rangeproofs_validation".to_string(), Some(json!({ "rproofs": rproofs, "rproofs_total": rproofs_total })), ), SyncStatus::TxHashsetKernelsValidation { kernels, kernels_total, } => ( "txhashset_kernels_validation".to_string(), Some(json!({ "kernels": kernels, "kernels_total": kernels_total })), ), SyncStatus::TxHashsetSave => ("txhashset_save".to_string(), None), SyncStatus::TxHashsetDone => ("txhashset_done".to_string(), None), 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), } }