diff --git a/Cargo.toml b/Cargo.toml index f92d48e1f..251069d7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ grin_grin = { path = "./grin" } grin_config = { path = "./config" } grin_core = { path = "./core" } grin_pow = { path = "./pow"} -grin_p2p = { path = "./p2p"} grin_util = { path = "./util"} blake2-rfc = "~0.2.17" clap = "^2.23.3" diff --git a/api/src/handlers.rs b/api/src/handlers.rs index 85f71a992..e66e0fdd3 100644 --- a/api/src/handlers.rs +++ b/api/src/handlers.rs @@ -317,6 +317,25 @@ impl Handler for PeerGetHandler { } } +// Status handler. Post a summary of the server status +// GET /v1/status +pub struct StatusHandler { + pub chain: Arc, + pub peers: p2p::Peers, +} + +impl StatusHandler { + fn get_status(&self) -> Status { + Status::from_tip_and_peers(self.chain.head().unwrap(), self.peers.peer_count()) + } +} + +impl Handler for StatusHandler { + fn handle(&self, _req: &mut Request) -> IronResult { + json_response(&self.get_status()) + } +} + // Chain handler. Get the head details. // GET /v1/chain pub struct ChainHandler { @@ -440,15 +459,12 @@ where tx.inputs.len(), tx.outputs.len() ); - - let res = self.tx_pool - .write() - .unwrap() - .add_to_memory_pool(source, tx); + + let res = self.tx_pool.write().unwrap().add_to_memory_pool(source, tx); match res { Ok(()) => Ok(Response::with(status::Ok)), - Err(e) => Err(IronError::from(Error::Argument(format!("{:?}", e)))) + Err(e) => Err(IronError::from(Error::Argument(format!("{:?}", e)))), } } } @@ -498,6 +514,10 @@ pub fn start_rest_apis( let chain_tip_handler = ChainHandler { chain: chain.clone(), }; + let status_handler = StatusHandler { + chain: chain.clone(), + peers: peers.clone(), + }; let sumtree_handler = SumTreeHandler { chain: chain.clone(), }; @@ -524,6 +544,7 @@ pub fn start_rest_apis( "get blocks".to_string(), "get chain".to_string(), "get chain/utxos".to_string(), + "get status".to_string(), "get sumtrees/roots".to_string(), "get sumtrees/lastutxos?n=10".to_string(), "get sumtrees/lastrangeproofs".to_string(), @@ -544,6 +565,7 @@ pub fn start_rest_apis( blocks: get "/blocks/*" => block_handler, chain_tip: get "/chain" => chain_tip_handler, chain_utxos: get "/chain/utxos/*" => utxo_handler, + status: get "/status" => status_handler, sumtree_roots: get "/sumtrees/*" => sumtree_handler, pool_info: get "/pool" => pool_info_handler, pool_push: post "/pool/push" => pool_push_handler, diff --git a/api/src/types.rs b/api/src/types.rs index 59a7d92ed..acd022f7e 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use core::{core, global}; use core::core::hash::Hashed; use chain; +use p2p; use util::secp::pedersen; use rest::*; use util; @@ -44,6 +45,30 @@ impl Tip { } } +/// Status page containing different server information +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Status { + // The protocol version + pub protocol_version: u32, + // The user user agent + pub user_agent: String, + // The current number of connections + pub connections: u32, + // The state of the current fork Tip + pub tip: Tip, +} + +impl Status { + pub fn from_tip_and_peers(current_tip: chain::Tip, connections: u32) -> Status { + Status { + protocol_version: p2p::msg::PROTOCOL_VERSION, + user_agent: p2p::msg::USER_AGENT.to_string(), + connections: connections, + tip: Tip::from_tip(current_tip), + } + } +} + /// Sumtrees #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SumTrees { @@ -88,7 +113,7 @@ impl SumTreeNode { .get_block_header_by_output_commit(&elem_output.1.commit) .map_err(|_| Error::NotFound); // Need to call further method to check if output is spent - let mut output = OutputPrintable::from_output(&elem_output.1, &header.unwrap(),true); + let mut output = OutputPrintable::from_output(&elem_output.1, &header.unwrap(), true); if let Ok(_) = chain.get_unspent(&elem_output.1.commit) { output.spent = false; } @@ -148,8 +173,12 @@ pub struct Output { } impl Output { - pub fn from_output(output: &core::Output, block_header: &core::BlockHeader, - include_proof:bool, include_switch: bool) -> Output { + pub fn from_output( + output: &core::Output, + block_header: &core::BlockHeader, + include_proof: bool, + include_switch: bool, + ) -> Output { let (output_type, lock_height) = match output.features { x if x.contains(core::transaction::COINBASE_OUTPUT) => ( OutputType::Coinbase, @@ -196,7 +225,11 @@ pub struct OutputPrintable { } impl OutputPrintable { - pub fn from_output(output: &core::Output, block_header: &core::BlockHeader, include_proof_hash:bool) -> OutputPrintable { + pub fn from_output( + output: &core::Output, + block_header: &core::BlockHeader, + include_proof_hash: bool, + ) -> OutputPrintable { let (output_type, lock_height) = match output.features { x if x.contains(core::transaction::COINBASE_OUTPUT) => ( OutputType::Coinbase, @@ -214,7 +247,7 @@ impl OutputPrintable { proof_hash: match include_proof_hash { true => Some(util::to_hex(output.proof.hash().to_vec())), false => None, - } + }, } } } @@ -257,7 +290,7 @@ impl TxKernelPrintable { fee: k.fee, lock_height: k.lock_height, excess: util::to_hex(k.excess.0.to_vec()), - excess_sig: util::to_hex(k.excess_sig.to_vec()) + excess_sig: util::to_hex(k.excess_sig.to_vec()), } } } @@ -322,7 +355,7 @@ impl BlockHeaderPrintable { kernel_root: util::to_hex(h.kernel_root.to_vec()), nonce: h.nonce, difficulty: h.difficulty.into_num(), - total_difficulty: h.total_difficulty.into_num() + total_difficulty: h.total_difficulty.into_num(), } } } @@ -342,15 +375,18 @@ pub struct BlockPrintable { impl BlockPrintable { pub fn from_block(block: &core::Block) -> BlockPrintable { - let inputs = block.inputs + let inputs = block + .inputs .iter() .map(|input| util::to_hex((input.0).0.to_vec())) .collect(); - let outputs = block.outputs + let outputs = block + .outputs .iter() .map(|output| OutputPrintable::from_output(output, &block.header, true)) .collect(); - let kernels = block.kernels + let kernels = block + .kernels .iter() .map(|kernel| TxKernelPrintable::from_txkernel(kernel)) .collect(); diff --git a/src/bin/client.rs b/src/bin/client.rs index 348310f17..e16f212cb 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -12,43 +12,47 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate grin_p2p as p2p; extern crate term; use api; use grin::ServerConfig; pub fn show_status(config: &ServerConfig) { - println!(); - let title=format!("Grin Server Status "); - let mut t = term::stdout().unwrap(); - let mut e = term::stdout().unwrap(); - t.fg(term::color::MAGENTA).unwrap(); - writeln!(t, "{}", title).unwrap(); - writeln!(t, "--------------------------").unwrap(); - t.reset().unwrap(); - writeln!(e, "Protocol version: {}", p2p::msg::PROTOCOL_VERSION).unwrap(); - writeln!(e, "User agent: {}", p2p::msg::USER_AGENT).unwrap(); - match get_tip_from_node(config) { - Ok(tip) => { - writeln!(e, "Chain height: {}", tip.height).unwrap(); - writeln!(e, "Total difficulty: {}", tip.total_difficulty).unwrap(); - writeln!(e, "Last block pushed: {}", tip.last_block_pushed).unwrap() - } - Err(_) => writeln!(e, "WARNING: Client failed to get data. Is your `grin server` offline or broken?").unwrap() - }; - e.reset().unwrap(); - println!(); + println!(); + let title = format!("Grin Server Status "); + let mut t = term::stdout().unwrap(); + let mut e = term::stdout().unwrap(); + t.fg(term::color::MAGENTA).unwrap(); + writeln!(t, "{}", title).unwrap(); + writeln!(t, "--------------------------").unwrap(); + t.reset().unwrap(); + match get_status_from_node(config) { + Ok(status) => { + writeln!(e, "Protocol version: {}", status.protocol_version).unwrap(); + writeln!(e, "User agent: {}", status.user_agent).unwrap(); + writeln!(e, "Connections: {}", status.connections).unwrap(); + writeln!(e, "Chain height: {}", status.tip.height).unwrap(); + writeln!(e, "Last block hash: {}", status.tip.last_block_pushed).unwrap(); + writeln!(e, "Previous block hash: {}", status.tip.prev_block_to_last).unwrap(); + writeln!(e, "Total difficulty: {}", status.tip.total_difficulty).unwrap() + } + Err(_) => writeln!( + e, + "WARNING: Client failed to get data. Is your `grin server` offline or broken?" + ).unwrap(), + }; + e.reset().unwrap(); + println!(); } -fn get_tip_from_node(config: &ServerConfig) -> Result { - let url = format!("http://{}/v1/chain", config.api_http_addr); - api::client::get::(url.as_str()).map_err(|e| Error::API(e)) +fn get_status_from_node(config: &ServerConfig) -> Result { + let url = format!("http://{}/v1/status", config.api_http_addr); + api::client::get::(url.as_str()).map_err(|e| Error::API(e)) } /// Error type wrapping underlying module errors. #[derive(Debug)] enum Error { /// Error originating from HTTP API calls. - API(api::Error) + API(api::Error), }