ledger-tool: optionally `graph` vote states
This commit is contained in:
parent
633950e029
commit
0fff5b8bf8
|
@ -399,8 +399,58 @@ fn render_dot(dot: String, output_file: &str, output_format: &str) -> io::Result
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum GraphVoteAccountMode {
|
||||
Disabled,
|
||||
LastOnly,
|
||||
WithHistory,
|
||||
}
|
||||
|
||||
impl GraphVoteAccountMode {
|
||||
const DISABLED: &'static str = "disabled";
|
||||
const LAST_ONLY: &'static str = "last-only";
|
||||
const WITH_HISTORY: &'static str = "with-history";
|
||||
const ALL_MODE_STRINGS: &'static [&'static str] =
|
||||
&[Self::DISABLED, Self::LAST_ONLY, Self::WITH_HISTORY];
|
||||
|
||||
fn is_enabled(&self) -> bool {
|
||||
!matches!(self, Self::Disabled)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for GraphVoteAccountMode {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
Self::Disabled => Self::DISABLED,
|
||||
Self::LastOnly => Self::LAST_ONLY,
|
||||
Self::WithHistory => Self::WITH_HISTORY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GraphVoteAccountMode {
|
||||
fn default() -> Self {
|
||||
Self::Disabled
|
||||
}
|
||||
}
|
||||
|
||||
struct GraphVoteAccountModeError(String);
|
||||
|
||||
impl FromStr for GraphVoteAccountMode {
|
||||
type Err = GraphVoteAccountModeError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
Self::DISABLED => Ok(Self::Disabled),
|
||||
Self::LAST_ONLY => Ok(Self::LastOnly),
|
||||
Self::WITH_HISTORY => Ok(Self::WithHistory),
|
||||
_ => Err(GraphVoteAccountModeError(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GraphConfig {
|
||||
include_all_votes: bool,
|
||||
vote_account_mode: GraphVoteAccountMode,
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
|
@ -555,36 +605,60 @@ fn graph_forks(bank_forks: &BankForks, config: &GraphConfig) -> String {
|
|||
validator_votes.remove(last_vote_slot);
|
||||
});
|
||||
|
||||
dot.push(format!(
|
||||
r#" "last vote {}"[shape=box,label="Latest validator vote: {}\nstake: {} SOL\nroot slot: {}\nvote history:\n{}"];"#,
|
||||
node_pubkey,
|
||||
node_pubkey,
|
||||
lamports_to_sol(*stake),
|
||||
vote_state.root_slot.unwrap_or(0),
|
||||
vote_state
|
||||
.votes
|
||||
.iter()
|
||||
.map(|vote| format!("slot {} (conf={})", vote.slot, vote.confirmation_count))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
));
|
||||
let maybe_styled_last_vote_slot = styled_slots.get(last_vote_slot);
|
||||
if maybe_styled_last_vote_slot.is_none() {
|
||||
if *last_vote_slot < lowest_last_vote_slot {
|
||||
lowest_last_vote_slot = *last_vote_slot;
|
||||
lowest_total_stake = *total_stake;
|
||||
}
|
||||
absent_votes += 1;
|
||||
absent_stake += stake;
|
||||
};
|
||||
|
||||
dot.push(format!(
|
||||
r#" "last vote {}" -> "{}" [style=dashed,label="latest vote"];"#,
|
||||
node_pubkey,
|
||||
if styled_slots.contains(last_vote_slot) {
|
||||
last_vote_slot.to_string()
|
||||
} else {
|
||||
if *last_vote_slot < lowest_last_vote_slot {
|
||||
lowest_last_vote_slot = *last_vote_slot;
|
||||
lowest_total_stake = *total_stake;
|
||||
}
|
||||
absent_votes += 1;
|
||||
absent_stake += stake;
|
||||
if config.vote_account_mode.is_enabled() {
|
||||
let vote_history =
|
||||
if matches!(config.vote_account_mode, GraphVoteAccountMode::WithHistory) {
|
||||
format!(
|
||||
"vote history:\n{}",
|
||||
vote_state
|
||||
.votes
|
||||
.iter()
|
||||
.map(|vote| format!(
|
||||
"slot {} (conf={})",
|
||||
vote.slot, vote.confirmation_count
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"last vote slot: {}",
|
||||
vote_state
|
||||
.votes
|
||||
.back()
|
||||
.map(|vote| vote.slot.to_string())
|
||||
.unwrap_or_else(|| "none".to_string())
|
||||
)
|
||||
};
|
||||
dot.push(format!(
|
||||
r#" "last vote {}"[shape=box,label="Latest validator vote: {}\nstake: {} SOL\nroot slot: {}\n{}"];"#,
|
||||
node_pubkey,
|
||||
node_pubkey,
|
||||
lamports_to_sol(*stake),
|
||||
vote_state.root_slot.unwrap_or(0),
|
||||
vote_history,
|
||||
));
|
||||
|
||||
"...".to_string()
|
||||
},
|
||||
));
|
||||
dot.push(format!(
|
||||
r#" "last vote {}" -> "{}" [style=dashed,label="latest vote"];"#,
|
||||
node_pubkey,
|
||||
if let Some(styled_last_vote_slot) = maybe_styled_last_vote_slot {
|
||||
styled_last_vote_slot.to_string()
|
||||
} else {
|
||||
"...".to_string()
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Annotate the final "..." node with absent vote and stake information
|
||||
|
@ -1170,6 +1244,7 @@ fn main() {
|
|||
let default_bootstrap_validator_stake_lamports = &sol_to_lamports(0.5)
|
||||
.max(rent.minimum_balance(StakeState::size_of()))
|
||||
.to_string();
|
||||
let default_graph_vote_account_mode = GraphVoteAccountMode::default();
|
||||
|
||||
let mut measure_total_execution_time = Measure::start("ledger tool");
|
||||
let matches = App::new(crate_name!())
|
||||
|
@ -1477,6 +1552,15 @@ fn main() {
|
|||
.takes_value(true)
|
||||
.help("Output file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("vote_account_mode")
|
||||
.long("vote-account-mode")
|
||||
.takes_value(true)
|
||||
.value_name("MODE")
|
||||
.default_value(default_graph_vote_account_mode.as_ref())
|
||||
.possible_values(GraphVoteAccountMode::ALL_MODE_STRINGS)
|
||||
.help("Specify if and how to graph vote accounts. Enabling will incur significant rendering overhead, especially `with-history`")
|
||||
)
|
||||
).subcommand(
|
||||
SubCommand::with_name("create-snapshot")
|
||||
.about("Create a new ledger snapshot")
|
||||
|
@ -2415,6 +2499,11 @@ fn main() {
|
|||
let output_file = value_t_or_exit!(arg_matches, "graph_filename", String);
|
||||
let graph_config = GraphConfig {
|
||||
include_all_votes: arg_matches.is_present("include_all_votes"),
|
||||
vote_account_mode: value_t_or_exit!(
|
||||
arg_matches,
|
||||
"vote_account_mode",
|
||||
GraphVoteAccountMode
|
||||
),
|
||||
};
|
||||
|
||||
let process_options = ProcessOptions {
|
||||
|
|
Loading…
Reference in New Issue