From 598e5f58d5122125397e822bc1a158166107943a Mon Sep 17 00:00:00 2001 From: sakridge Date: Thu, 12 Nov 2020 13:48:34 -0800 Subject: [PATCH] Add wait for max stake command (#13532) --- cli/src/cli.rs | 13 +++++++++++ cli/src/cluster_query.rs | 21 ++++++++++++++++++ client/src/rpc_client.rs | 32 ++++++++++++++++++++++++++++ local-cluster/tests/local_cluster.rs | 20 +++++++++++++++++ system-test/automation_utils.sh | 12 +---------- 5 files changed, 87 insertions(+), 11 deletions(-) diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 2d49403611..e1ee8aa66b 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -149,6 +149,9 @@ pub enum CliCommand { limit: usize, show_transactions: bool, }, + WaitForMaxStake { + max_stake_percent: f32, + }, // Nonce commands AuthorizeNonceAccount { nonce_account: Pubkey, @@ -624,6 +627,13 @@ pub fn parse_command( signers, }) } + ("wait-for-max-stake", Some(matches)) => { + let max_stake_percent = value_t_or_exit!(matches, "max_percent", f32); + Ok(CliCommandInfo { + command: CliCommand::WaitForMaxStake { max_stake_percent }, + signers: vec![], + }) + } // Stake Commands ("create-stake-account", Some(matches)) => { parse_stake_create_account(matches, default_signer, wallet_manager) @@ -1565,6 +1575,9 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { *use_lamports_unit, vote_account_pubkeys.as_deref(), ), + CliCommand::WaitForMaxStake { max_stake_percent } => { + process_wait_for_max_stake(&rpc_client, config, *max_stake_percent) + } CliCommand::ShowValidators { use_lamports_unit } => { process_show_validators(&rpc_client, config, *use_lamports_unit) } diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 82e87efc38..3237c8b50f 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -318,6 +318,17 @@ impl ClusterQuerySubCommands for App<'_, '_> { .help("Display the full transactions"), ) ) + .subcommand( + SubCommand::with_name("wait-for-max-stake") + .about("Wait for the max stake of any one node to drop below a percentage of total.") + .arg( + Arg::with_name("max_percent") + .long("max-percent") + .value_name("PERCENT") + .takes_value(true) + .index(1), + ), + ) } } @@ -1400,6 +1411,16 @@ pub fn process_show_stakes( .formatted_string(&CliStakeVec::new(stake_accounts))) } +pub fn process_wait_for_max_stake( + rpc_client: &RpcClient, + config: &CliConfig, + max_stake_percent: f32, +) -> ProcessResult { + let now = std::time::Instant::now(); + rpc_client.wait_for_max_stake(config.commitment, max_stake_percent)?; + Ok(format!("Done waiting, took: {}s", now.elapsed().as_secs())) +} + pub fn process_show_validators( rpc_client: &RpcClient, config: &CliConfig, diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 3c256400e7..273d46730d 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -353,6 +353,38 @@ impl RpcClient { self.send(RpcRequest::GetVoteAccounts, json!([commitment_config])) } + pub fn wait_for_max_stake( + &self, + commitment: CommitmentConfig, + max_stake_percent: f32, + ) -> ClientResult<()> { + let mut current_percent; + loop { + let vote_accounts = self.get_vote_accounts_with_commitment(commitment)?; + + let mut max = 0; + let total_active_stake = vote_accounts + .current + .iter() + .chain(vote_accounts.delinquent.iter()) + .map(|vote_account| { + max = std::cmp::max(max, vote_account.activated_stake); + vote_account.activated_stake + }) + .sum::(); + current_percent = 100f32 * max as f32 / total_active_stake as f32; + if current_percent < max_stake_percent { + break; + } + info!( + "Waiting for stake to drop below {} current: {:.1}", + max_stake_percent, current_percent + ); + sleep(Duration::from_secs(10)); + } + Ok(()) + } + pub fn get_cluster_nodes(&self) -> ClientResult> { self.send(RpcRequest::GetClusterNodes, Value::Null) } diff --git a/local-cluster/tests/local_cluster.rs b/local-cluster/tests/local_cluster.rs index 3bfcf3eceb..1cd43a56b1 100644 --- a/local-cluster/tests/local_cluster.rs +++ b/local-cluster/tests/local_cluster.rs @@ -1340,6 +1340,26 @@ fn test_faulty_node(faulty_node_type: BroadcastStageType) { cluster.check_for_new_roots(16, &"test_faulty_node"); } +#[test] +fn test_wait_for_max_stake() { + solana_logger::setup(); + let mut validator_config = ValidatorConfig::default(); + validator_config.rpc_config.enable_validator_exit = true; + let config = ClusterConfig { + cluster_lamports: 10_000, + node_stakes: vec![100; 4], + validator_configs: vec![validator_config; 4], + ..ClusterConfig::default() + }; + let cluster = LocalCluster::new(&config); + let client = RpcClient::new_socket(cluster.entry_point_info.rpc); + + assert!(client + .wait_for_max_stake(CommitmentConfig::default(), 33.0f32) + .is_ok()); + assert!(client.get_slot().unwrap() > 10); +} + #[test] // Test that when a leader is leader for banks B_i..B_{i+n}, and B_i is not // votable, then B_{i+1} still chains to B_i diff --git a/system-test/automation_utils.sh b/system-test/automation_utils.sh index 136399d6e7..b9d6bc8077 100755 --- a/system-test/automation_utils.sh +++ b/system-test/automation_utils.sh @@ -69,18 +69,8 @@ function wait_for_bootstrap_validator_stake_drop { source "${REPO_ROOT}"/net/common.sh loadConfigFile - while true; do # shellcheck disable=SC2154 - bootstrap_validator_validator_info="$(ssh "${sshOptions[@]}" "${validatorIpList[0]}" '$HOME/.cargo/bin/solana --url http://127.0.0.1:8899 validators | grep "$($HOME/.cargo/bin/solana-keygen pubkey ~/solana/config/bootstrap-validator/identity.json)"')" - bootstrap_validator_stake_percentage="$(echo "$bootstrap_validator_validator_info" | awk '{gsub(/[\(,\),\%]/,""); print $9}')" - - if [[ $(echo "$bootstrap_validator_stake_percentage < $max_stake" | bc) -ne 0 ]]; then - echo "Bootstrap validator stake has fallen below $max_stake to $bootstrap_validator_stake_percentage" - break - fi - echo "Max bootstrap validator stake: $max_stake. Current stake: $bootstrap_validator_stake_percentage. Sleeping 30s for stake to distribute." - sleep 30 - done + ssh "${sshOptions[@]}" "${validatorIpList[0]}" '$HOME/.cargo/bin/solana wait-for-max-stake $max_stake --url http://127.0.0.1:8899' } function get_slot {