Update ``grin client status`` (#599)

* Add status type
* Add /v1/status handler
* Updated client for API::Status type
* Remove unused dependency
* Moved user agent up
This commit is contained in:
Quentin Le Sceller 2018-01-11 00:25:48 -05:00 committed by Ignotus Peverell
parent ac5010e8f7
commit cbc40c9824
4 changed files with 103 additions and 42 deletions

View file

@ -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"

View file

@ -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<chain::Chain>,
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<Response> {
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<T>(
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<T>(
"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<T>(
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,

View file

@ -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();

View file

@ -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<api::Tip, Error> {
let url = format!("http://{}/v1/chain", config.api_http_addr);
api::client::get::<api::Tip>(url.as_str()).map_err(|e| Error::API(e))
fn get_status_from_node(config: &ServerConfig) -> Result<api::Status, Error> {
let url = format!("http://{}/v1/status", config.api_http_addr);
api::client::get::<api::Status>(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),
}