* cli: struct the tuples (cherry picked from commitb9eb6242f5
) * cli: add software version(s) to feature status (cherry picked from commit152da44b62
) # Conflicts: # cli/Cargo.toml * cli: sort feature status output (cherry picked from commit30d277b9fd
) * cli: improve feature status arithmatic readability (cherry picked from commitd98c8b861c
) Co-authored-by: Trent Nelson <trent@solana.com>
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -3717,6 +3717,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver-parser"
|
name = "semver-parser"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -4279,6 +4285,7 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
"pretty-hex",
|
"pretty-hex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"semver 1.0.4",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -26,6 +26,7 @@ humantime = "2.0.1"
|
|||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
pretty-hex = "0.2.1"
|
pretty-hex = "0.2.1"
|
||||||
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||||
|
semver = "1.0.4"
|
||||||
serde = "1.0.122"
|
serde = "1.0.122"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
|
@ -18,7 +18,12 @@ use solana_sdk::{
|
|||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, fmt, sync::Arc};
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
fmt,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum ForceActivation {
|
pub enum ForceActivation {
|
||||||
@ -222,7 +227,23 @@ pub fn process_feature_subcommand(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feature_set_stats(rpc_client: &RpcClient) -> Result<HashMap<u32, (f64, f32)>, ClientError> {
|
#[derive(Debug, Default)]
|
||||||
|
struct WorkingFeatureSetStatsEntry {
|
||||||
|
stake: u64,
|
||||||
|
rpc_nodes_count: u32,
|
||||||
|
software_versions: HashSet<Option<semver::Version>>,
|
||||||
|
}
|
||||||
|
type WorkingFeatureSetStats = HashMap<u32, WorkingFeatureSetStatsEntry>;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct FeatureSetStatsEntry {
|
||||||
|
stake_percent: f64,
|
||||||
|
rpc_nodes_percent: f32,
|
||||||
|
software_versions: Vec<Option<semver::Version>>,
|
||||||
|
}
|
||||||
|
type FeatureSetStats = HashMap<u32, FeatureSetStatsEntry>;
|
||||||
|
|
||||||
|
fn feature_set_stats(rpc_client: &RpcClient) -> Result<FeatureSetStats, ClientError> {
|
||||||
// Validator identity -> feature set
|
// Validator identity -> feature set
|
||||||
let feature_sets = rpc_client
|
let feature_sets = rpc_client
|
||||||
.get_cluster_nodes()?
|
.get_cluster_nodes()?
|
||||||
@ -232,6 +253,9 @@ fn feature_set_stats(rpc_client: &RpcClient) -> Result<HashMap<u32, (f64, f32)>,
|
|||||||
contact_info.pubkey,
|
contact_info.pubkey,
|
||||||
contact_info.feature_set,
|
contact_info.feature_set,
|
||||||
contact_info.rpc.is_some(),
|
contact_info.rpc.is_some(),
|
||||||
|
contact_info
|
||||||
|
.version
|
||||||
|
.and_then(|v| semver::Version::parse(&v).ok()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -253,33 +277,53 @@ fn feature_set_stats(rpc_client: &RpcClient) -> Result<HashMap<u32, (f64, f32)>,
|
|||||||
})
|
})
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
let mut feature_set_stats: HashMap<u32, (u64, u32)> = HashMap::new();
|
let mut feature_set_stats: WorkingFeatureSetStats = HashMap::new();
|
||||||
let mut total_rpc_nodes = 0;
|
let mut total_rpc_nodes = 0;
|
||||||
for (node_id, feature_set, is_rpc) in feature_sets {
|
for (node_id, feature_set, is_rpc, version) in feature_sets {
|
||||||
let feature_set = feature_set.unwrap_or(0);
|
let feature_set = feature_set.unwrap_or(0);
|
||||||
let feature_set_entry = feature_set_stats.entry(feature_set).or_default();
|
let feature_set_entry = feature_set_stats.entry(feature_set).or_default();
|
||||||
|
|
||||||
|
feature_set_entry.software_versions.insert(version);
|
||||||
|
|
||||||
if let Some(vote_stake) = vote_stakes.get(&node_id) {
|
if let Some(vote_stake) = vote_stakes.get(&node_id) {
|
||||||
feature_set_entry.0 += *vote_stake;
|
feature_set_entry.stake += *vote_stake;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_rpc {
|
if is_rpc {
|
||||||
feature_set_entry.1 += 1;
|
feature_set_entry.rpc_nodes_count += 1;
|
||||||
total_rpc_nodes += 1;
|
total_rpc_nodes += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(feature_set_stats
|
Ok(feature_set_stats
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(feature_set, (active_stake, is_rpc))| {
|
.filter_map(
|
||||||
let active_stake = active_stake as f64 * 100. / total_active_stake as f64;
|
|(
|
||||||
let is_rpc = is_rpc as f32 * 100. / total_rpc_nodes as f32;
|
feature_set,
|
||||||
if active_stake >= 0.001 || is_rpc >= 0.001 {
|
WorkingFeatureSetStatsEntry {
|
||||||
Some((feature_set, (active_stake, is_rpc)))
|
stake,
|
||||||
|
rpc_nodes_count,
|
||||||
|
software_versions,
|
||||||
|
},
|
||||||
|
)| {
|
||||||
|
let stake_percent = (stake as f64 / total_active_stake as f64) * 100.;
|
||||||
|
let rpc_nodes_percent = (rpc_nodes_count as f32 / total_rpc_nodes as f32) * 100.;
|
||||||
|
let mut software_versions = software_versions.into_iter().collect::<Vec<_>>();
|
||||||
|
software_versions.sort();
|
||||||
|
if stake_percent >= 0.001 || rpc_nodes_percent >= 0.001 {
|
||||||
|
Some((
|
||||||
|
feature_set,
|
||||||
|
FeatureSetStatsEntry {
|
||||||
|
stake_percent,
|
||||||
|
rpc_nodes_percent,
|
||||||
|
software_versions,
|
||||||
|
},
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +335,13 @@ fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<boo
|
|||||||
|
|
||||||
let (stake_allowed, rpc_allowed) = feature_set_stats
|
let (stake_allowed, rpc_allowed) = feature_set_stats
|
||||||
.get(&my_feature_set)
|
.get(&my_feature_set)
|
||||||
.map(|(stake_percent, rpc_percent)| (*stake_percent >= 95., *rpc_percent >= 95.))
|
.map(
|
||||||
|
|FeatureSetStatsEntry {
|
||||||
|
stake_percent,
|
||||||
|
rpc_nodes_percent,
|
||||||
|
..
|
||||||
|
}| (*stake_percent >= 95., *rpc_nodes_percent >= 95.),
|
||||||
|
)
|
||||||
.unwrap_or((false, false));
|
.unwrap_or((false, false));
|
||||||
|
|
||||||
if !stake_allowed && !rpc_allowed && !quiet {
|
if !stake_allowed && !rpc_allowed && !quiet {
|
||||||
@ -322,33 +372,96 @@ fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<boo
|
|||||||
"\n\n{}",
|
"\n\n{}",
|
||||||
style(format!("Tool Feature Set: {}", my_feature_set)).bold()
|
style(format!("Tool Feature Set: {}", my_feature_set)).bold()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut feature_set_stats = feature_set_stats.into_iter().collect::<Vec<_>>();
|
||||||
|
feature_set_stats.sort_by(|l, r| {
|
||||||
|
match l.1.software_versions[0]
|
||||||
|
.cmp(&r.1.software_versions[0])
|
||||||
|
.reverse()
|
||||||
|
{
|
||||||
|
Ordering::Equal => {
|
||||||
|
match l
|
||||||
|
.1
|
||||||
|
.stake_percent
|
||||||
|
.partial_cmp(&r.1.stake_percent)
|
||||||
|
.unwrap()
|
||||||
|
.reverse()
|
||||||
|
{
|
||||||
|
Ordering::Equal => {
|
||||||
|
l.1.rpc_nodes_percent
|
||||||
|
.partial_cmp(&r.1.rpc_nodes_percent)
|
||||||
|
.unwrap()
|
||||||
|
.reverse()
|
||||||
|
}
|
||||||
|
o => o,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o => o,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let software_versions_title = "Software Version";
|
||||||
let feature_set_title = "Feature Set";
|
let feature_set_title = "Feature Set";
|
||||||
let stake_percent_title = "Stake";
|
let stake_percent_title = "Stake";
|
||||||
let rpc_percent_title = "RPC";
|
let rpc_percent_title = "RPC";
|
||||||
let mut stats_output = Vec::new();
|
let mut stats_output = Vec::new();
|
||||||
|
let mut max_software_versions_len = software_versions_title.len();
|
||||||
let mut max_feature_set_len = feature_set_title.len();
|
let mut max_feature_set_len = feature_set_title.len();
|
||||||
let mut max_stake_percent_len = stake_percent_title.len();
|
let mut max_stake_percent_len = stake_percent_title.len();
|
||||||
let mut max_rpc_percent_len = rpc_percent_title.len();
|
let mut max_rpc_percent_len = rpc_percent_title.len();
|
||||||
for (feature_set, (stake_percent, rpc_percent)) in feature_set_stats.iter() {
|
for (
|
||||||
let me = *feature_set == my_feature_set;
|
feature_set,
|
||||||
let feature_set = if *feature_set == 0 {
|
FeatureSetStatsEntry {
|
||||||
|
stake_percent,
|
||||||
|
rpc_nodes_percent,
|
||||||
|
software_versions,
|
||||||
|
},
|
||||||
|
) in feature_set_stats.into_iter()
|
||||||
|
{
|
||||||
|
let me = feature_set == my_feature_set;
|
||||||
|
let feature_set = if feature_set == 0 {
|
||||||
"unknown".to_string()
|
"unknown".to_string()
|
||||||
} else {
|
} else {
|
||||||
feature_set.to_string()
|
feature_set.to_string()
|
||||||
};
|
};
|
||||||
let stake_percent = format!("{:.2}%", stake_percent);
|
let stake_percent = format!("{:.2}%", stake_percent);
|
||||||
let rpc_percent = format!("{:.2}%", rpc_percent);
|
let rpc_percent = format!("{:.2}%", rpc_nodes_percent);
|
||||||
|
|
||||||
|
let mut has_unknown = false;
|
||||||
|
let mut software_versions = software_versions
|
||||||
|
.iter()
|
||||||
|
.filter_map(|v| {
|
||||||
|
if v.is_none() {
|
||||||
|
has_unknown = true;
|
||||||
|
}
|
||||||
|
v.as_ref()
|
||||||
|
})
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if has_unknown {
|
||||||
|
software_versions.push("unknown".to_string());
|
||||||
|
}
|
||||||
|
let software_versions = software_versions.join(", ");
|
||||||
|
max_software_versions_len = max_software_versions_len.max(software_versions.len());
|
||||||
|
|
||||||
max_feature_set_len = max_feature_set_len.max(feature_set.len());
|
max_feature_set_len = max_feature_set_len.max(feature_set.len());
|
||||||
max_stake_percent_len = max_stake_percent_len.max(stake_percent.len());
|
max_stake_percent_len = max_stake_percent_len.max(stake_percent.len());
|
||||||
max_rpc_percent_len = max_rpc_percent_len.max(rpc_percent.len());
|
max_rpc_percent_len = max_rpc_percent_len.max(rpc_percent.len());
|
||||||
|
|
||||||
stats_output.push((feature_set, stake_percent, rpc_percent, me));
|
stats_output.push((
|
||||||
|
software_versions,
|
||||||
|
feature_set,
|
||||||
|
stake_percent,
|
||||||
|
rpc_percent,
|
||||||
|
me,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
style(format!(
|
style(format!(
|
||||||
"{1:<0$} {3:<2$} {5:<4$}",
|
"{1:<0$} {3:<2$} {5:<4$} {7:<6$}",
|
||||||
|
max_software_versions_len,
|
||||||
|
software_versions_title,
|
||||||
max_feature_set_len,
|
max_feature_set_len,
|
||||||
feature_set_title,
|
feature_set_title,
|
||||||
max_stake_percent_len,
|
max_stake_percent_len,
|
||||||
@ -358,9 +471,11 @@ fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<boo
|
|||||||
))
|
))
|
||||||
.bold(),
|
.bold(),
|
||||||
);
|
);
|
||||||
for (feature_set, stake_percent, rpc_percent, me) in stats_output {
|
for (software_versions, feature_set, stake_percent, rpc_percent, me) in stats_output {
|
||||||
println!(
|
println!(
|
||||||
"{1:>0$} {3:>2$} {5:>4$} {6}",
|
"{1:<0$} {3:>2$} {5:>4$} {7:>6$} {8}",
|
||||||
|
max_software_versions_len,
|
||||||
|
software_versions,
|
||||||
max_feature_set_len,
|
max_feature_set_len,
|
||||||
feature_set,
|
feature_set,
|
||||||
max_stake_percent_len,
|
max_stake_percent_len,
|
||||||
|
Reference in New Issue
Block a user