Add show-block-production command

This commit is contained in:
Michael Vines 2019-12-18 11:38:54 -07:00
parent 05664d150b
commit ff171baa67
4 changed files with 166 additions and 4 deletions

View File

@ -405,7 +405,7 @@ Returns the leader schedule for an epoch
#### Parameters:
* `slot` - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot. If unspecified, the leader schedule for the current epoch is fetch
* `slot` - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot. If unspecified, the leader schedule for the current epoch is fetched
* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
#### Results:

View File

@ -21,7 +21,7 @@ use solana_faucet::faucet::request_airdrop_transaction;
use solana_faucet::faucet_mock::request_airdrop_transaction;
use solana_sdk::{
bpf_loader,
clock::Slot,
clock::{Epoch, Slot},
commitment_config::CommitmentConfig,
fee_calculator::FeeCalculator,
hash::Hash,
@ -106,6 +106,10 @@ pub enum CliCommand {
timeout: Duration,
commitment_config: CommitmentConfig,
},
ShowBlockProduction {
epoch: Option<Epoch>,
slot_limit: Option<u64>,
},
ShowGossip,
ShowValidators {
use_lamports_unit: bool,
@ -336,6 +340,7 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Box<dyn
("get-slot", Some(matches)) => parse_get_slot(matches),
("get-transaction-count", Some(matches)) => parse_get_transaction_count(matches),
("ping", Some(matches)) => parse_cluster_ping(matches),
("show-block-production", Some(matches)) => parse_show_block_production(matches),
("show-gossip", Some(_matches)) => Ok(CliCommandInfo {
command: CliCommand::ShowGossip,
require_keypair: false,
@ -1126,6 +1131,9 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
timeout,
commitment_config,
),
CliCommand::ShowBlockProduction { epoch, slot_limit } => {
process_show_block_production(&rpc_client, *epoch, *slot_limit)
}
CliCommand::ShowGossip => process_show_gossip(&rpc_client),
CliCommand::ShowValidators { use_lamports_unit } => {
process_show_validators(&rpc_client, *use_lamports_unit)

View File

@ -5,7 +5,7 @@ use crate::{
},
display::println_name_value,
};
use clap::{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 indicatif::{ProgressBar, ProgressStyle};
use solana_clap_utils::{input_parsers::*, input_validators::*};
@ -20,7 +20,7 @@ use solana_sdk::{
system_transaction,
};
use std::{
collections::VecDeque,
collections::{HashMap, VecDeque},
net::SocketAddr,
thread::sleep,
time::{Duration, Instant},
@ -149,6 +149,22 @@ impl ClusterQuerySubCommands for App<'_, '_> {
),
),
)
.subcommand(
SubCommand::with_name("show-block-production")
.about("Show information about block production")
.arg(
Arg::with_name("epoch")
.long("epoch")
.takes_value(true)
.help("Epoch to show block production for [default: current epoch]"),
)
.arg(
Arg::with_name("slot_limit")
.long("slot-limit")
.takes_value(true)
.help("Limit results to this many slots from the end of the epoch [default: full epoch]"),
),
)
.subcommand(
SubCommand::with_name("show-gossip")
.about("Show the current gossip network nodes"),
@ -394,6 +410,138 @@ pub fn process_get_slot(
Ok(slot.to_string())
}
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let epoch = value_t!(matches, "epoch", Epoch).ok();
let slot_limit = value_t!(matches, "slot_limit", u64).ok();
Ok(CliCommandInfo {
command: CliCommand::ShowBlockProduction { epoch, slot_limit },
require_keypair: false,
})
}
pub fn process_show_block_production(
rpc_client: &RpcClient,
epoch: Option<Epoch>,
slot_limit: Option<u64>,
) -> ProcessResult {
let epoch_schedule = rpc_client.get_epoch_schedule()?;
let epoch_info = rpc_client.get_epoch_info_with_commitment(CommitmentConfig::max())?;
let epoch = epoch.unwrap_or(epoch_info.epoch);
if epoch > epoch_info.epoch {
return Err(format!("Epoch {} is in the future", epoch).into());
}
let end_slot = std::cmp::min(
epoch_info.absolute_slot,
epoch_schedule.get_last_slot_in_epoch(epoch),
);
let start_slot = {
let start_slot = epoch_schedule.get_first_slot_in_epoch(epoch);
std::cmp::max(
end_slot.saturating_sub(slot_limit.unwrap_or(start_slot)),
start_slot,
)
};
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Connecting...");
progress_bar.set_message(&format!("Fetching leader schedule for epoch {}...", epoch));
let leader_schedule = rpc_client
.get_leader_schedule_with_commitment(Some(start_slot), CommitmentConfig::max())?;
if leader_schedule.is_none() {
return Err(format!("Unable to fetch leader schedule for slot {}", start_slot).into());
}
let leader_schedule = leader_schedule.unwrap();
progress_bar.set_message(&format!(
"Fetching confirmed blocks between slots {} and {}...",
start_slot, end_slot
));
let confirmed_blocks = rpc_client.get_confirmed_blocks(start_slot, Some(end_slot))?;
let total_slots = (end_slot - start_slot + 1) as usize;
let total_blocks = confirmed_blocks.len();
let total_slots_missed = total_slots - total_blocks;
let mut leader_slot_count = HashMap::new();
let mut leader_missed_slots = HashMap::new();
for slot_index in 0..total_slots {
let leader = {
let mut leader = None;
for (pubkey, leader_slots) in leader_schedule.iter() {
if leader_slots.contains(&slot_index) {
leader = Some(pubkey);
break;
}
}
leader.unwrap()
};
let slot = start_slot + slot_index as u64;
let remaining_slots = total_slots - slot_index;
progress_bar.set_message(&format!(
"Checking slot {} ({:.}% done, {} slots remaining)...",
slot,
100 * slot_index / total_slots,
remaining_slots,
));
let slot_count = leader_slot_count.entry(leader).or_insert(0);
*slot_count += 1;
let missed_slots = leader_missed_slots.entry(leader).or_insert(0);
if !confirmed_blocks.contains(&slot) {
*missed_slots += 1;
}
}
progress_bar.finish_and_clear();
println!(
"\n{}",
style(format!(
" {:<44} {:>15} {:>15} {:>15} {:>23}",
"Identity Pubkey",
"Leader Slots",
"Blocks Produced",
"Missed Slots",
"Missed Block Percentage",
))
.bold()
);
for (leader, leader_slots) in leader_slot_count.iter() {
let missed_slots = leader_missed_slots.get(leader).unwrap();
let blocks_produced = leader_slots - missed_slots;
println!(
" {:<44} {:>15} {:>15} {:>15} {:>22.2}%",
leader,
leader_slots,
blocks_produced,
missed_slots,
*missed_slots as f64 / *leader_slots as f64 * 100.
);
}
println!(
"\n {:<44} {:>15} {:>15} {:>15} {:>22.2}%",
format!("Epoch {} total:", epoch),
total_slots,
total_blocks,
total_slots_missed,
total_slots_missed as f64 / total_slots as f64 * 100.
);
println!(
" (using data from {} slots: {} to {})",
total_slots, start_slot, end_slot
);
Ok("".to_string())
}
pub fn process_get_transaction_count(
rpc_client: &RpcClient,
commitment_config: &CommitmentConfig,

View File

@ -19,6 +19,12 @@ impl CommitmentConfig {
}
}
pub fn max() -> Self {
Self {
commitment: CommitmentLevel::Max,
}
}
pub fn ok(&self) -> Option<Self> {
if self == &Self::default() {
None