som: Add --quality-block-producer-percentage (#10462)

automerge
This commit is contained in:
Michael Vines
2020-06-08 22:15:59 -07:00
committed by GitHub
parent 9d07a23c46
commit 4cfb7588d9
2 changed files with 43 additions and 28 deletions

View File

@ -2,7 +2,7 @@ use clap::{crate_description, crate_name, crate_version, value_t, value_t_or_exi
use log::*; use log::*;
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::{keypair_of, pubkey_of}, input_parsers::{keypair_of, pubkey_of},
input_validators::{is_keypair, is_pubkey_or_keypair, is_url}, input_validators::{is_keypair, is_pubkey_or_keypair, is_url, is_valid_percentage},
}; };
use solana_client::{ use solana_client::{
client_error, rpc_client::RpcClient, rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS, client_error, rpc_client::RpcClient, rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS,
@ -34,7 +34,7 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
mod whitelist; mod validator_list;
struct Config { struct Config {
json_rpc_url: String, json_rpc_url: String,
@ -42,18 +42,19 @@ struct Config {
source_stake_address: Pubkey, source_stake_address: Pubkey,
authorized_staker: Keypair, authorized_staker: Keypair,
/// Only validators with an identity pubkey in this whitelist will be staked /// Only validators with an identity pubkey in this validator_list will be staked
whitelist: HashSet<Pubkey>, validator_list: HashSet<Pubkey>,
dry_run: bool, dry_run: bool,
/// Amount of lamports to stake any validator in the whitelist that is not delinquent /// Amount of lamports to stake any validator in the validator_list that is not delinquent
baseline_stake_amount: u64, baseline_stake_amount: u64,
/// Amount of additional lamports to stake quality block producers in the whitelist /// Amount of additional lamports to stake quality block producers in the validator_list
bonus_stake_amount: u64, bonus_stake_amount: u64,
/// Quality validators produce a block in more than this percentage of their leader slots /// Quality validators produce a block at least this percentage of their leader slots over the
/// previous epoch
quality_block_producer_percentage: usize, quality_block_producer_percentage: usize,
/// A delinquent validator gets this number of slots of grace (from the current slot) before it /// A delinquent validator gets this number of slots of grace (from the current slot) before it
@ -98,8 +99,8 @@ fn get_config() -> Config {
.help("Name of the cluster to operate on") .help("Name of the cluster to operate on")
) )
.arg( .arg(
Arg::with_name("whitelist_file") Arg::with_name("validator_list_file")
.long("whitelist") .long("validator-list")
.value_name("FILE") .value_name("FILE")
.required(true) .required(true)
.takes_value(true) .takes_value(true)
@ -128,7 +129,17 @@ fn get_config() -> Config {
.validator(is_keypair) .validator(is_keypair)
.required(true) .required(true)
.takes_value(true) .takes_value(true)
) .get_matches(); )
.arg(
Arg::with_name("quality_block_producer_percentage")
.long("quality-block-producer-percentage")
.value_name("PERCENTAGE")
.takes_value(true)
.default_value("75")
.validator(is_valid_percentage)
.help("Quality validators produce a block in at least this percentage of their leader slots over the previous epoch")
)
.get_matches();
let config = if let Some(config_file) = matches.value_of("config_file") { let config = if let Some(config_file) = matches.value_of("config_file") {
solana_cli_config::Config::load(config_file).unwrap_or_default() solana_cli_config::Config::load(config_file).unwrap_or_default()
@ -140,56 +151,59 @@ fn get_config() -> Config {
let authorized_staker = keypair_of(&matches, "authorized_staker").unwrap(); let authorized_staker = keypair_of(&matches, "authorized_staker").unwrap();
let dry_run = !matches.is_present("confirm"); let dry_run = !matches.is_present("confirm");
let cluster = value_t!(matches, "cluster", String).unwrap_or_else(|_| "unknown".into()); let cluster = value_t!(matches, "cluster", String).unwrap_or_else(|_| "unknown".into());
let quality_block_producer_percentage =
value_t_or_exit!(matches, "quality_block_producer_percentage", usize);
let (json_rpc_url, whitelist) = match cluster.as_str() { let (json_rpc_url, validator_list) = match cluster.as_str() {
"mainnet-beta" => ( "mainnet-beta" => (
"http://api.mainnet-beta.solana.com".into(), "http://api.mainnet-beta.solana.com".into(),
whitelist::mainnet_beta_validators(), validator_list::mainnet_beta_validators(),
), ),
"testnet" => ( "testnet" => (
"http://testnet.solana.com".into(), "http://testnet.solana.com".into(),
whitelist::testnet_validators(), validator_list::testnet_validators(),
), ),
"unknown" => { "unknown" => {
let whitelist_file = File::open(value_t_or_exit!(matches, "whitelist_file", PathBuf)) let validator_list_file =
File::open(value_t_or_exit!(matches, "validator_list_file", PathBuf))
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
error!("Unable to open whitelist: {}", err); error!("Unable to open validator_list: {}", err);
process::exit(1); process::exit(1);
}); });
let whitelist = serde_yaml::from_reader::<_, Vec<String>>(whitelist_file) let validator_list = serde_yaml::from_reader::<_, Vec<String>>(validator_list_file)
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
error!("Unable to read whitelist: {}", err); error!("Unable to read validator_list: {}", err);
process::exit(1); process::exit(1);
}) })
.into_iter() .into_iter()
.map(|p| { .map(|p| {
Pubkey::from_str(&p).unwrap_or_else(|err| { Pubkey::from_str(&p).unwrap_or_else(|err| {
error!("Invalid whitelist pubkey '{}': {}", p, err); error!("Invalid validator_list pubkey '{}': {}", p, err);
process::exit(1); process::exit(1);
}) })
}) })
.collect(); .collect();
( (
value_t!(matches, "json_rpc_url", String).unwrap_or_else(|_| config.json_rpc_url), value_t!(matches, "json_rpc_url", String).unwrap_or_else(|_| config.json_rpc_url),
whitelist, validator_list,
) )
} }
_ => unreachable!(), _ => unreachable!(),
}; };
let whitelist = whitelist.into_iter().collect::<HashSet<_>>(); let validator_list = validator_list.into_iter().collect::<HashSet<_>>();
let config = Config { let config = Config {
json_rpc_url, json_rpc_url,
cluster, cluster,
source_stake_address, source_stake_address,
authorized_staker, authorized_staker,
whitelist, validator_list,
dry_run, dry_run,
baseline_stake_amount: sol_to_lamports(5000.), baseline_stake_amount: sol_to_lamports(5000.),
bonus_stake_amount: sol_to_lamports(50_000.), bonus_stake_amount: sol_to_lamports(50_000.),
delinquent_grace_slot_distance: 21600, // ~24 hours worth of slots at 2.5 slots per second delinquent_grace_slot_distance: 21600, // ~24 hours worth of slots at 2.5 slots per second
quality_block_producer_percentage: 75, quality_block_producer_percentage,
}; };
info!("RPC URL: {}", config.json_rpc_url); info!("RPC URL: {}", config.json_rpc_url);
@ -279,7 +293,8 @@ fn classify_block_producers(
); );
if validator_slots > 0 { if validator_slots > 0 {
let validator_identity = Pubkey::from_str(&validator_identity)?; let validator_identity = Pubkey::from_str(&validator_identity)?;
if validator_blocks * 100 / validator_slots > config.quality_block_producer_percentage { if validator_blocks * 100 / validator_slots >= config.quality_block_producer_percentage
{
quality_block_producers.insert(validator_identity); quality_block_producers.insert(validator_identity);
} else { } else {
poor_block_producers.insert(validator_identity); poor_block_producers.insert(validator_identity);
@ -563,7 +578,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
let (quality_block_producers, _poor_block_producers) = let (quality_block_producers, _poor_block_producers) =
classify_block_producers(&rpc_client, &config, last_epoch)?; classify_block_producers(&rpc_client, &config, last_epoch)?;
// Fetch vote account status for all the whitelisted validators // Fetch vote account status for all the validator_listed validators
let vote_account_status = rpc_client.get_vote_accounts()?; let vote_account_status = rpc_client.get_vote_accounts()?;
let vote_account_info = vote_account_status let vote_account_info = vote_account_status
.current .current
@ -571,7 +586,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.chain(vote_account_status.delinquent.into_iter()) .chain(vote_account_status.delinquent.into_iter())
.filter_map(|vai| { .filter_map(|vai| {
let node_pubkey = Pubkey::from_str(&vai.node_pubkey).ok()?; let node_pubkey = Pubkey::from_str(&vai.node_pubkey).ok()?;
if config.whitelist.contains(&node_pubkey) { if config.validator_list.contains(&node_pubkey) {
Some(vai) Some(vai)
} else { } else {
None None