diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 67d47945ad..ce3f571e8b 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -94,6 +94,25 @@ impl RpcClient { }) } + pub fn get_version(&self) -> io::Result { + let response = self + .client + .send(&RpcRequest::GetVersion, None, 0) + .map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("GetVersion request failure: {:?}", err), + ) + })?; + + serde_json::to_string(&response).map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("GetVersion parse failure: {}", err), + ) + }) + } + pub fn send_and_confirm_transaction( &self, transaction: &mut Transaction, diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index bc6a75e4ab..f39cec5a27 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -9,18 +9,19 @@ pub enum RpcRequest { GetAccountInfo, GetBalance, GetClusterNodes, + GetEpochVoteAccounts, GetNumBlocksSinceSignatureConfirmation, GetProgramAccounts, GetRecentBlockhash, GetSignatureStatus, GetSlot, GetSlotLeader, - GetEpochVoteAccounts, GetStorageTurn, GetStorageTurnRate, GetSlotsPerSegment, GetStoragePubkeysForSlot, GetTransactionCount, + GetVersion, RegisterNode, RequestAirdrop, SendTransaction, @@ -37,6 +38,7 @@ impl RpcRequest { RpcRequest::GetAccountInfo => "getAccountInfo", RpcRequest::GetBalance => "getBalance", RpcRequest::GetClusterNodes => "getClusterNodes", + RpcRequest::GetEpochVoteAccounts => "getEpochVoteAccounts", RpcRequest::GetNumBlocksSinceSignatureConfirmation => { "getNumBlocksSinceSignatureConfirmation" } @@ -45,12 +47,12 @@ impl RpcRequest { RpcRequest::GetSignatureStatus => "getSignatureStatus", RpcRequest::GetSlot => "getSlot", RpcRequest::GetSlotLeader => "getSlotLeader", - RpcRequest::GetEpochVoteAccounts => "getEpochVoteAccounts", RpcRequest::GetStorageTurn => "getStorageTurn", RpcRequest::GetStorageTurnRate => "getStorageTurnRate", RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment", RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot", RpcRequest::GetTransactionCount => "getTransactionCount", + RpcRequest::GetVersion => "getVersion", RpcRequest::RegisterNode => "registerNode", RpcRequest::RequestAirdrop => "requestAirdrop", RpcRequest::SendTransaction => "sendTransaction", diff --git a/wallet/src/display.rs b/wallet/src/display.rs new file mode 100644 index 0000000000..2dd0e3ee8d --- /dev/null +++ b/wallet/src/display.rs @@ -0,0 +1,11 @@ +use console::style; + +// Pretty print a "name value" +pub fn println_name_value(name: &str, value: &str) { + let styled_value = if value == "" { + style("(not set)").italic() + } else { + style(value) + }; + println!("{} {}", style(name).bold(), styled_value); +} diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 6704269352..fc2cecae67 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -2,4 +2,5 @@ extern crate lazy_static; pub mod config; +pub mod display; pub mod wallet; diff --git a/wallet/src/main.rs b/wallet/src/main.rs index 4a12892f6c..07a18d9875 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -2,6 +2,7 @@ use clap::{crate_description, crate_name, crate_version, Arg, ArgGroup, ArgMatch use console::style; use solana_sdk::signature::{gen_keypair_file, read_keypair, KeypairUtil}; use solana_wallet::config::{self, Config}; +use solana_wallet::display::println_name_value; use solana_wallet::wallet::{app, parse_command, process_command, WalletConfig, WalletError}; use std::error; @@ -120,16 +121,6 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result Result<(), String> { match url::Url::parse(&string) { diff --git a/wallet/src/wallet.rs b/wallet/src/wallet.rs index baf83408d4..100558f21e 100644 --- a/wallet/src/wallet.rs +++ b/wallet/src/wallet.rs @@ -1,9 +1,11 @@ +use crate::display::println_name_value; use chrono::prelude::*; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; +use console::style; use log::*; use num_traits::FromPrimitive; use serde_json; -use serde_json::json; +use serde_json::{json, Value}; use solana_budget_api; use solana_budget_api::budget_instruction; use solana_budget_api::budget_state::BudgetError; @@ -63,6 +65,7 @@ pub enum WalletCommand { Deploy(String), GetSlot, GetTransactionCount, + GetVersion, // Pay(lamports, to, timestamp, timestamp_pubkey, witness(es), cancelable) Pay( u64, @@ -362,6 +365,7 @@ pub fn parse_command( }; Ok(WalletCommand::TimeElapsed(to, process_id, dt)) } + ("cluster-version", Some(_matches)) => Ok(WalletCommand::GetVersion), ("", None) => { eprintln!("{}", matches.usage()); Err(WalletError::CommandNotRecognized( @@ -1040,6 +1044,23 @@ fn process_witness( Ok(signature_str.to_string()) } +fn process_get_version(rpc_client: &RpcClient, config: &WalletConfig) -> ProcessResult { + let remote_version: Value = serde_json::from_str(&rpc_client.get_version()?)?; + println!( + "{} {}", + style("Cluster versions from:").bold(), + config.json_rpc_url + ); + if let Some(versions) = remote_version.as_object() { + for (key, value) in versions.iter() { + if let Some(value_string) = value.as_str() { + println_name_value(&format!("* {}:", key), &value_string); + } + } + } + Ok("".to_string()) +} + pub fn process_command(config: &WalletConfig) -> ProcessResult { if let WalletCommand::Address = config.command { // Get address of this client @@ -1220,6 +1241,9 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult { WalletCommand::Witness(to, pubkey) => { process_witness(&rpc_client, config, drone_addr, &to, &pubkey) } + + // Return software version of wallet and cluster entrypoint node + WalletCommand::GetVersion => process_get_version(&rpc_client, config), } } @@ -1795,6 +1819,10 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .help("Optional arbitrary timestamp to apply"), ), ) + .subcommand( + SubCommand::with_name("cluster-version") + .about("Get the version of the cluster entrypoint"), + ) } #[cfg(test)]