145 lines
4.8 KiB
Rust
145 lines
4.8 KiB
Rust
use crate::utils;
|
|
use log::*;
|
|
use solana_client::rpc_client::RpcClient;
|
|
use solana_sdk::{
|
|
clock::Epoch,
|
|
epoch_info::EpochInfo,
|
|
genesis_config::GenesisConfig,
|
|
stake_history::StakeHistoryEntry,
|
|
sysvar::{
|
|
stake_history::{self, StakeHistory},
|
|
Sysvar,
|
|
},
|
|
};
|
|
use solana_stake_program::config::Config as StakeConfig;
|
|
use std::{thread::sleep, time::Duration};
|
|
|
|
fn calculate_stake_warmup(mut stake_entry: StakeHistoryEntry, stake_config: &StakeConfig) -> u64 {
|
|
let mut epochs = 0;
|
|
loop {
|
|
let percent_warming_up =
|
|
stake_entry.activating as f64 / stake_entry.effective.max(1) as f64;
|
|
let percent_cooling_down =
|
|
stake_entry.deactivating as f64 / stake_entry.effective.max(1) as f64;
|
|
debug!(
|
|
"epoch +{}: stake warming up {:.1}%, cooling down {:.1}% ",
|
|
epochs,
|
|
percent_warming_up * 100.,
|
|
percent_cooling_down * 100.
|
|
);
|
|
|
|
if (percent_warming_up < 0.05) && (percent_cooling_down < 0.05) {
|
|
break;
|
|
}
|
|
let warmup_cooldown_rate = stake_config.warmup_cooldown_rate;
|
|
let max_warmup_stake = (stake_entry.effective as f64 * warmup_cooldown_rate) as u64;
|
|
let warmup_stake = stake_entry.activating.min(max_warmup_stake);
|
|
stake_entry.effective += warmup_stake;
|
|
stake_entry.activating -= warmup_stake;
|
|
|
|
let max_cooldown_stake = (stake_entry.effective as f64 * warmup_cooldown_rate) as u64;
|
|
let cooldown_stake = stake_entry.deactivating.min(max_cooldown_stake);
|
|
stake_entry.effective -= cooldown_stake;
|
|
stake_entry.deactivating -= cooldown_stake;
|
|
debug!(
|
|
"epoch +{}: stake warming up {}, cooling down {}",
|
|
epochs, warmup_stake, cooldown_stake
|
|
);
|
|
|
|
epochs += 1;
|
|
}
|
|
info!("95% stake warmup will take {} epochs", epochs);
|
|
epochs
|
|
}
|
|
|
|
fn stake_history_entry(epoch: Epoch, rpc_client: &RpcClient) -> Option<StakeHistoryEntry> {
|
|
let stake_history_account = rpc_client.get_account(&stake_history::id()).ok()?;
|
|
let stake_history = StakeHistory::from_account(&stake_history_account)?;
|
|
stake_history.get(&epoch).cloned()
|
|
}
|
|
|
|
/// Wait until stake warms up and return the current epoch
|
|
pub fn wait_for_warm_up(
|
|
activation_epoch: Epoch,
|
|
mut epoch_info: EpochInfo,
|
|
rpc_client: &RpcClient,
|
|
stake_config: &StakeConfig,
|
|
genesis_config: &GenesisConfig,
|
|
notifier: &solana_notifier::Notifier,
|
|
) {
|
|
// Sleep until activation_epoch has finished
|
|
if epoch_info.epoch <= activation_epoch {
|
|
notifier.send(&format!(
|
|
"Waiting until epoch {} is finished...",
|
|
activation_epoch
|
|
));
|
|
|
|
utils::sleep_until_epoch(
|
|
rpc_client,
|
|
notifier,
|
|
genesis_config,
|
|
epoch_info.absolute_slot,
|
|
activation_epoch + 1,
|
|
);
|
|
}
|
|
|
|
loop {
|
|
epoch_info = rpc_client.get_epoch_info().unwrap_or_else(|err| {
|
|
utils::bail(
|
|
notifier,
|
|
&format!("Error: get_epoch_info RPC call failed: {}", err),
|
|
);
|
|
});
|
|
|
|
let current_slot = epoch_info.absolute_slot;
|
|
info!("Current slot is {}", current_slot);
|
|
|
|
let current_epoch = epoch_info.epoch;
|
|
let latest_epoch = current_epoch - 1;
|
|
debug!(
|
|
"Fetching stake history entry for epoch: {}...",
|
|
latest_epoch
|
|
);
|
|
|
|
if let Some(stake_entry) = stake_history_entry(latest_epoch, &rpc_client) {
|
|
debug!("Stake history entry: {:?}", &stake_entry);
|
|
let warm_up_epochs = calculate_stake_warmup(stake_entry, stake_config);
|
|
let stake_warmed_up_epoch = latest_epoch + warm_up_epochs;
|
|
if stake_warmed_up_epoch > current_epoch {
|
|
notifier.send(&format!(
|
|
"Waiting until epoch {} for stake to warmup (current epoch is {})...",
|
|
stake_warmed_up_epoch, current_epoch
|
|
));
|
|
utils::sleep_until_epoch(
|
|
rpc_client,
|
|
notifier,
|
|
genesis_config,
|
|
current_slot,
|
|
stake_warmed_up_epoch,
|
|
);
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
warn!(
|
|
"Failed to fetch stake history entry for epoch: {}",
|
|
latest_epoch
|
|
);
|
|
sleep(Duration::from_secs(5));
|
|
}
|
|
|
|
let latest_slot = rpc_client.get_slot().unwrap_or_else(|err| {
|
|
utils::bail(
|
|
notifier,
|
|
&format!("Error: get_slot RPC call 3 failed: {}", err),
|
|
);
|
|
});
|
|
if current_slot == latest_slot {
|
|
utils::bail(
|
|
notifier,
|
|
&format!("Error: Slot did not advance from {}", current_slot),
|
|
);
|
|
}
|
|
}
|
|
}
|