diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 3f2be908a4..920a416cd6 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -193,6 +193,10 @@ pub enum CliCommand { program_id: Pubkey, }, Fees, + FirstAvailableBlock, + GetBlock { + slot: Slot, + }, GetBlockTime { slot: Option, }, @@ -639,6 +643,11 @@ pub fn parse_command( command: CliCommand::Fees, signers: vec![], }), + ("first-available-block", Some(_matches)) => Ok(CliCommandInfo { + command: CliCommand::FirstAvailableBlock, + signers: vec![], + }), + ("block", Some(matches)) => parse_get_block(matches), ("block-time", Some(matches)) => parse_get_block_time(matches), ("epoch-info", Some(matches)) => parse_get_epoch_info(matches), ("genesis-hash", Some(_matches)) => Ok(CliCommandInfo { @@ -1865,6 +1874,8 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { program_id, } => process_create_address_with_seed(config, from_pubkey.as_ref(), &seed, &program_id), CliCommand::Fees => process_fees(&rpc_client, config), + CliCommand::FirstAvailableBlock => process_first_available_block(&rpc_client), + CliCommand::GetBlock { slot } => process_get_block(&rpc_client, config, *slot), CliCommand::GetBlockTime { slot } => process_get_block_time(&rpc_client, config, *slot), CliCommand::GetEpoch => process_get_epoch(&rpc_client, config), CliCommand::GetEpochInfo => process_get_epoch_info(&rpc_client, config), diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 6ed6afd363..f818378db1 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -57,6 +57,19 @@ pub trait ClusterQuerySubCommands { impl ClusterQuerySubCommands for App<'_, '_> { fn cluster_query_subcommands(self) -> Self { self.subcommand( + SubCommand::with_name("block") + .about("Get a confirmed block") + .arg( + Arg::with_name("slot") + .long("slot") + .validator(is_slot) + .value_name("SLOT") + .takes_value(true) + .index(1) + .required(true), + ), + ) + .subcommand( SubCommand::with_name("catchup") .about("Wait for a validator to catch up to the cluster") .arg( @@ -91,6 +104,10 @@ impl ClusterQuerySubCommands for App<'_, '_> { .about("Get the version of the cluster entrypoint"), ) .subcommand(SubCommand::with_name("fees").about("Display current cluster fees")) + .subcommand( + SubCommand::with_name("first-available-block") + .about("Get the first available block in the storage"), + ) .subcommand(SubCommand::with_name("block-time") .about("Get estimated production time of a block") .alias("get-block-time") @@ -342,6 +359,14 @@ pub fn parse_cluster_ping( }) } +pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result { + let slot = value_t_or_exit!(matches, "slot", Slot); + Ok(CliCommandInfo { + command: CliCommand::GetBlock { slot }, + signers: vec![], + }) +} + pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result { let slot = value_of(matches, "slot"); Ok(CliCommandInfo { @@ -614,6 +639,11 @@ pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult Ok(config.output_format.formatted_string(&fees)) } +pub fn process_first_available_block(rpc_client: &RpcClient) -> ProcessResult { + let first_available_block = rpc_client.get_first_available_block()?; + Ok(format!("{}", first_available_block)) +} + pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult { let epoch_info = rpc_client.get_epoch_info()?; let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index; @@ -649,6 +679,42 @@ pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult { Ok("".to_string()) } +pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot) -> ProcessResult { + let block = + rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?; + + println!("Slot: {}", slot); + println!("Parent Slot: {}", block.parent_slot); + println!("Blockhash: {}", block.blockhash); + println!("Previous Blockhash: {}", block.previous_blockhash); + if block.block_time.is_some() { + println!("Block Time: {:?}", block.block_time); + } + if !block.rewards.is_empty() { + println!("Rewards:",); + for reward in block.rewards { + println!( + " {:<44}: {}", + reward.pubkey, + if reward.lamports > 0 { + format!("◎{}", lamports_to_sol(reward.lamports as u64)) + } else { + format!("◎-{}", lamports_to_sol(reward.lamports.abs() as u64)) + } + ); + } + } + for (index, transaction_with_meta) in block.transactions.iter().enumerate() { + println!("Transaction {}:", index); + println_transaction( + &transaction_with_meta.transaction.decode().unwrap(), + &transaction_with_meta.meta, + " ", + ); + } + Ok("".to_string()) +} + pub fn process_get_block_time( rpc_client: &RpcClient, config: &CliConfig, diff --git a/cli/src/display.rs b/cli/src/display.rs index fb9ac3a90d..977abfc4b7 100644 --- a/cli/src/display.rs +++ b/cli/src/display.rs @@ -162,7 +162,7 @@ pub fn write_transaction( )?; writeln!( w, - "{} Fee: {} SOL", + "{} Fee: ◎{}", prefix, lamports_to_sol(transaction_status.fee) )?; @@ -179,7 +179,7 @@ pub fn write_transaction( if pre == post { writeln!( w, - "{} Account {} balance: {} SOL", + "{} Account {} balance: ◎{}", prefix, i, lamports_to_sol(*pre) @@ -187,7 +187,7 @@ pub fn write_transaction( } else { writeln!( w, - "{} Account {} balance: {} SOL -> {} SOL", + "{} Account {} balance: ◎{} -> ◎{}", prefix, i, lamports_to_sol(*pre), diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 155ed107a6..e4603612bc 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -709,6 +709,10 @@ impl RpcClient { .into()) } + pub fn get_first_available_block(&self) -> ClientResult { + self.send(RpcRequest::GetFirstAvailableBlock, Value::Null) + } + pub fn get_genesis_hash(&self) -> ClientResult { let hash_str: String = self.send(RpcRequest::GetGenesisHash, Value::Null)?; let hash = hash_str.parse().map_err(|_| { diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 97019577ef..7bf60294e5 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -21,6 +21,7 @@ pub enum RpcRequest { GetFeeCalculatorForBlockhash, GetFeeRateGovernor, GetFees, + GetFirstAvailableBlock, GetGenesisHash, GetIdentity, GetInflationGovernor, @@ -74,6 +75,7 @@ impl fmt::Display for RpcRequest { RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash", RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor", RpcRequest::GetFees => "getFees", + RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock", RpcRequest::GetGenesisHash => "getGenesisHash", RpcRequest::GetIdentity => "getIdentity", RpcRequest::GetInflationGovernor => "getInflationGovernor",