Cli: add cluster-date subcommand, and make block-time slot optional (#9878)
* Add CliBlockTime struct * Add cli cluster-date subcommand * Make slot param optional; also jsonify * Make prints prettier
This commit is contained in:
@ -179,6 +179,7 @@ pub enum CliCommand {
|
|||||||
commitment_config: CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
follow: bool,
|
follow: bool,
|
||||||
},
|
},
|
||||||
|
ClusterDate,
|
||||||
ClusterVersion,
|
ClusterVersion,
|
||||||
CreateAddressWithSeed {
|
CreateAddressWithSeed {
|
||||||
from_pubkey: Option<Pubkey>,
|
from_pubkey: Option<Pubkey>,
|
||||||
@ -187,7 +188,7 @@ pub enum CliCommand {
|
|||||||
},
|
},
|
||||||
Fees,
|
Fees,
|
||||||
GetBlockTime {
|
GetBlockTime {
|
||||||
slot: Slot,
|
slot: Option<Slot>,
|
||||||
},
|
},
|
||||||
GetEpochInfo {
|
GetEpochInfo {
|
||||||
commitment_config: CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
@ -587,6 +588,10 @@ pub fn parse_command(
|
|||||||
let response = match matches.subcommand() {
|
let response = match matches.subcommand() {
|
||||||
// Cluster Query Commands
|
// Cluster Query Commands
|
||||||
("catchup", Some(matches)) => parse_catchup(matches, wallet_manager),
|
("catchup", Some(matches)) => parse_catchup(matches, wallet_manager),
|
||||||
|
("cluster-date", Some(_matches)) => Ok(CliCommandInfo {
|
||||||
|
command: CliCommand::ClusterDate,
|
||||||
|
signers: vec![],
|
||||||
|
}),
|
||||||
("cluster-version", Some(_matches)) => Ok(CliCommandInfo {
|
("cluster-version", Some(_matches)) => Ok(CliCommandInfo {
|
||||||
command: CliCommand::ClusterVersion,
|
command: CliCommand::ClusterVersion,
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
@ -1685,6 +1690,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
*commitment_config,
|
*commitment_config,
|
||||||
*follow,
|
*follow,
|
||||||
),
|
),
|
||||||
|
CliCommand::ClusterDate => process_cluster_date(&rpc_client, config),
|
||||||
CliCommand::ClusterVersion => process_cluster_version(&rpc_client),
|
CliCommand::ClusterVersion => process_cluster_version(&rpc_client),
|
||||||
CliCommand::CreateAddressWithSeed {
|
CliCommand::CreateAddressWithSeed {
|
||||||
from_pubkey,
|
from_pubkey,
|
||||||
@ -1692,7 +1698,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
program_id,
|
program_id,
|
||||||
} => process_create_address_with_seed(config, from_pubkey.as_ref(), &seed, &program_id),
|
} => process_create_address_with_seed(config, from_pubkey.as_ref(), &seed, &program_id),
|
||||||
CliCommand::Fees => process_fees(&rpc_client),
|
CliCommand::Fees => process_fees(&rpc_client),
|
||||||
CliCommand::GetBlockTime { slot } => process_get_block_time(&rpc_client, *slot),
|
CliCommand::GetBlockTime { slot } => process_get_block_time(&rpc_client, config, *slot),
|
||||||
CliCommand::GetGenesisHash => process_get_genesis_hash(&rpc_client),
|
CliCommand::GetGenesisHash => process_get_genesis_hash(&rpc_client),
|
||||||
CliCommand::GetEpochInfo { commitment_config } => {
|
CliCommand::GetEpochInfo { commitment_config } => {
|
||||||
process_get_epoch_info(&rpc_client, config, *commitment_config)
|
process_get_epoch_info(&rpc_client, config, *commitment_config)
|
||||||
|
@ -839,3 +839,26 @@ impl From<&Lockout> for CliLockout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CliBlockTime {
|
||||||
|
pub slot: Slot,
|
||||||
|
pub timestamp: UnixTimestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CliBlockTime {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
writeln_name_value(f, "Block:", &self.slot.to_string())?;
|
||||||
|
writeln_name_value(
|
||||||
|
f,
|
||||||
|
"Date:",
|
||||||
|
&format!(
|
||||||
|
"{} (UnixTimestamp: {})",
|
||||||
|
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.timestamp, 0), Utc)
|
||||||
|
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||||
|
self.timestamp
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
cli::{check_account_for_fee, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
cli::{check_account_for_fee, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||||
cli_output::{
|
cli_output::*,
|
||||||
CliBlockProduction, CliBlockProductionEntry, CliEpochInfo, CliKeyedStakeState,
|
|
||||||
CliSlotStatus, CliStakeVec, CliValidator, CliValidators,
|
|
||||||
},
|
|
||||||
display::println_name_value,
|
display::println_name_value,
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
|
|
||||||
use clap::{value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
use clap::{value_t, value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
||||||
use console::{style, Emoji};
|
use console::{style, Emoji};
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
@ -24,7 +20,7 @@ use solana_client::{
|
|||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
clock::{self, Slot},
|
clock::{self, Clock, Slot},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
epoch_schedule::Epoch,
|
epoch_schedule::Epoch,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
@ -33,6 +29,7 @@ use solana_sdk::{
|
|||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
system_instruction,
|
system_instruction,
|
||||||
|
sysvar::{self, Sysvar},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
@ -81,6 +78,10 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
)
|
)
|
||||||
.arg(commitment_arg()),
|
.arg(commitment_arg()),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("cluster-date")
|
||||||
|
.about("Get current cluster date, computed from genesis creation time and network time")
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("cluster-version")
|
SubCommand::with_name("cluster-version")
|
||||||
.about("Get the version of the cluster entrypoint"),
|
.about("Get the version of the cluster entrypoint"),
|
||||||
@ -94,7 +95,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
.index(1)
|
.index(1)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("SLOT")
|
.value_name("SLOT")
|
||||||
.required(true)
|
|
||||||
.help("Slot number of the block to query")
|
.help("Slot number of the block to query")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -310,7 +310,7 @@ pub fn parse_cluster_ping(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let slot = value_t_or_exit!(matches, "slot", u64);
|
let slot = value_of(matches, "slot");
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetBlockTime { slot },
|
command: CliCommand::GetBlockTime { slot },
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
@ -518,6 +518,24 @@ pub fn process_catchup(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||||
|
let result = rpc_client
|
||||||
|
.get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::default())?;
|
||||||
|
if let Some(clock_account) = result.value {
|
||||||
|
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
||||||
|
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||||
|
})?;
|
||||||
|
let block_time = CliBlockTime {
|
||||||
|
slot: result.context.slot,
|
||||||
|
timestamp: clock.unix_timestamp,
|
||||||
|
};
|
||||||
|
config.output_format.formatted_print(&block_time);
|
||||||
|
Ok("".to_string())
|
||||||
|
} else {
|
||||||
|
Err(format!("AccountNotFound: pubkey={}", sysvar::clock::id()).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn process_cluster_version(rpc_client: &RpcClient) -> ProcessResult {
|
pub fn process_cluster_version(rpc_client: &RpcClient) -> ProcessResult {
|
||||||
let remote_version = rpc_client.get_version()?;
|
let remote_version = rpc_client.get_version()?;
|
||||||
Ok(remote_version.solana_core)
|
Ok(remote_version.solana_core)
|
||||||
@ -567,15 +585,20 @@ pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
|
|||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_block_time(rpc_client: &RpcClient, slot: Slot) -> ProcessResult {
|
pub fn process_get_block_time(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
config: &CliConfig,
|
||||||
|
slot: Option<Slot>,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let slot = if let Some(slot) = slot {
|
||||||
|
slot
|
||||||
|
} else {
|
||||||
|
rpc_client.get_slot()?
|
||||||
|
};
|
||||||
let timestamp = rpc_client.get_block_time(slot)?;
|
let timestamp = rpc_client.get_block_time(slot)?;
|
||||||
let result = format!(
|
let block_time = CliBlockTime { slot, timestamp };
|
||||||
"{} (UnixTimestamp: {})",
|
config.output_format.formatted_print(&block_time);
|
||||||
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc)
|
Ok("".to_string())
|
||||||
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
|
||||||
timestamp
|
|
||||||
);
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_epoch_info(
|
pub fn process_get_epoch_info(
|
||||||
@ -1196,6 +1219,17 @@ mod tests {
|
|||||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
|
||||||
|
let test_cluster_version = test_commands
|
||||||
|
.clone()
|
||||||
|
.get_matches_from(vec!["test", "cluster-date"]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
|
||||||
|
CliCommandInfo {
|
||||||
|
command: CliCommand::ClusterDate,
|
||||||
|
signers: vec![],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let test_cluster_version = test_commands
|
let test_cluster_version = test_commands
|
||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "cluster-version"]);
|
.get_matches_from(vec!["test", "cluster-version"]);
|
||||||
@ -1224,7 +1258,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_get_block_time, &default_keypair_file, &mut None).unwrap(),
|
parse_command(&test_get_block_time, &default_keypair_file, &mut None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetBlockTime { slot },
|
command: CliCommand::GetBlockTime { slot: Some(slot) },
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user