@@ -13,6 +13,7 @@ use solana_client::{rpc_client::RpcClient, rpc_request::RpcVoteAccountInfo};
 | 
			
		||||
use solana_sdk::{
 | 
			
		||||
    clock::{self, Slot},
 | 
			
		||||
    commitment_config::CommitmentConfig,
 | 
			
		||||
    epoch_schedule::{Epoch, EpochSchedule},
 | 
			
		||||
    hash::Hash,
 | 
			
		||||
    pubkey::Pubkey,
 | 
			
		||||
    signature::{Keypair, KeypairUtil},
 | 
			
		||||
@@ -260,6 +261,20 @@ fn new_spinner_progress_bar() -> ProgressBar {
 | 
			
		||||
    progress_bar
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Aggregate epoch credit stats and return (total credits, total slots, total epochs)
 | 
			
		||||
pub fn aggregate_epoch_credits(
 | 
			
		||||
    epoch_credits: &[(Epoch, u64, u64)],
 | 
			
		||||
    epoch_schedule: &EpochSchedule,
 | 
			
		||||
) -> (u64, u64, u64) {
 | 
			
		||||
    epoch_credits
 | 
			
		||||
        .iter()
 | 
			
		||||
        .fold((0, 0, 0), |acc, (epoch, credits, prev_credits)| {
 | 
			
		||||
            let credits_earned = credits - prev_credits;
 | 
			
		||||
            let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
 | 
			
		||||
            (acc.0 + credits_earned, acc.1 + slots_in_epoch, acc.2 + 1)
 | 
			
		||||
        })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn process_catchup(rpc_client: &RpcClient, node_pubkey: &Pubkey) -> ProcessResult {
 | 
			
		||||
    let cluster_nodes = rpc_client.get_cluster_nodes()?;
 | 
			
		||||
 | 
			
		||||
@@ -550,6 +565,7 @@ pub fn process_show_gossip(rpc_client: &RpcClient) -> ProcessResult {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool) -> ProcessResult {
 | 
			
		||||
    let epoch_schedule = rpc_client.get_epoch_schedule()?;
 | 
			
		||||
    let vote_accounts = rpc_client.get_vote_accounts()?;
 | 
			
		||||
    let total_active_stake = vote_accounts
 | 
			
		||||
        .current
 | 
			
		||||
@@ -592,19 +608,21 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
 | 
			
		||||
    println!(
 | 
			
		||||
        "{}",
 | 
			
		||||
        style(format!(
 | 
			
		||||
            "  {:<44}  {:<44}  {}  {}  {}  {}",
 | 
			
		||||
            "  {:<44}  {:<44}  {}  {}  {}  {:>7}  {}",
 | 
			
		||||
            "Identity Pubkey",
 | 
			
		||||
            "Vote Account Pubkey",
 | 
			
		||||
            "Commission",
 | 
			
		||||
            "Last Vote",
 | 
			
		||||
            "Root Block",
 | 
			
		||||
            "Uptime",
 | 
			
		||||
            "Active Stake",
 | 
			
		||||
        ))
 | 
			
		||||
        .bold()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    fn print_vote_account(
 | 
			
		||||
        vote_account: &RpcVoteAccountInfo,
 | 
			
		||||
        vote_account: RpcVoteAccountInfo,
 | 
			
		||||
        epoch_schedule: &EpochSchedule,
 | 
			
		||||
        total_active_stake: f64,
 | 
			
		||||
        use_lamports_unit: bool,
 | 
			
		||||
        delinquent: bool,
 | 
			
		||||
@@ -616,8 +634,20 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
 | 
			
		||||
                format!("{}", v)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn uptime(epoch_credits: Vec<(Epoch, u64, u64)>, epoch_schedule: &EpochSchedule) -> String {
 | 
			
		||||
            let (total_credits, total_slots, _) =
 | 
			
		||||
                aggregate_epoch_credits(&epoch_credits, &epoch_schedule);
 | 
			
		||||
            if total_slots > 0 {
 | 
			
		||||
                let total_uptime = 100_f64 * total_credits as f64 / total_slots as f64;
 | 
			
		||||
                format!("{:.2}%", total_uptime)
 | 
			
		||||
            } else {
 | 
			
		||||
                "-".into()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        println!(
 | 
			
		||||
            "{} {:<44}  {:<44}  {:>9}%   {:>8}  {:>10}  {:>12}",
 | 
			
		||||
            "{} {:<44}  {:<44}  {:>9}%   {:>8}  {:>10}  {:>7}  {}",
 | 
			
		||||
            if delinquent {
 | 
			
		||||
                WARNING.to_string()
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -628,6 +658,7 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
 | 
			
		||||
            vote_account.commission,
 | 
			
		||||
            non_zero_or_dash(vote_account.last_vote),
 | 
			
		||||
            non_zero_or_dash(vote_account.root_slot),
 | 
			
		||||
            uptime(vote_account.epoch_credits, epoch_schedule),
 | 
			
		||||
            if vote_account.activated_stake > 0 {
 | 
			
		||||
                format!(
 | 
			
		||||
                    "{} ({:.2}%)",
 | 
			
		||||
@@ -640,11 +671,23 @@ pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for vote_account in vote_accounts.current.iter() {
 | 
			
		||||
        print_vote_account(vote_account, total_active_stake, use_lamports_unit, false);
 | 
			
		||||
    for vote_account in vote_accounts.current.into_iter() {
 | 
			
		||||
        print_vote_account(
 | 
			
		||||
            vote_account,
 | 
			
		||||
            &epoch_schedule,
 | 
			
		||||
            total_active_stake,
 | 
			
		||||
            use_lamports_unit,
 | 
			
		||||
            false,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    for vote_account in vote_accounts.delinquent.iter() {
 | 
			
		||||
        print_vote_account(vote_account, total_active_stake, use_lamports_unit, true);
 | 
			
		||||
    for vote_account in vote_accounts.delinquent.into_iter() {
 | 
			
		||||
        print_vote_account(
 | 
			
		||||
            vote_account,
 | 
			
		||||
            &epoch_schedule,
 | 
			
		||||
            total_active_stake,
 | 
			
		||||
            use_lamports_unit,
 | 
			
		||||
            true,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok("".to_string())
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,10 @@
 | 
			
		||||
use crate::cli::{
 | 
			
		||||
use crate::{
 | 
			
		||||
    cli::{
 | 
			
		||||
        build_balance_message, check_account_for_fee, check_unique_pubkeys,
 | 
			
		||||
    log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
 | 
			
		||||
        log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
 | 
			
		||||
        ProcessResult,
 | 
			
		||||
    },
 | 
			
		||||
    cluster_query::aggregate_epoch_credits,
 | 
			
		||||
};
 | 
			
		||||
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
 | 
			
		||||
use solana_clap_utils::{input_parsers::*, input_validators::*};
 | 
			
		||||
@@ -401,33 +405,37 @@ pub fn process_uptime(
 | 
			
		||||
    if !vote_state.votes.is_empty() {
 | 
			
		||||
        println!("Uptime:");
 | 
			
		||||
 | 
			
		||||
        let epoch_credits_vec: Vec<(u64, u64, u64)> = vote_state.epoch_credits().copied().collect();
 | 
			
		||||
 | 
			
		||||
        let epoch_credits = if let Some(x) = span {
 | 
			
		||||
            epoch_credits_vec.iter().rev().take(x as usize)
 | 
			
		||||
        let epoch_credits: Vec<(u64, u64, u64)> = if let Some(x) = span {
 | 
			
		||||
            vote_state
 | 
			
		||||
                .epoch_credits()
 | 
			
		||||
                .iter()
 | 
			
		||||
                .rev()
 | 
			
		||||
                .take(x as usize)
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .collect()
 | 
			
		||||
        } else {
 | 
			
		||||
            epoch_credits_vec.iter().rev().take(epoch_credits_vec.len())
 | 
			
		||||
            vote_state.epoch_credits().iter().rev().cloned().collect()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if aggregate {
 | 
			
		||||
            let (credits_earned, slots_in_epoch, epochs): (u64, u64, u64) =
 | 
			
		||||
                epoch_credits.fold((0, 0, 0), |acc, (epoch, credits, prev_credits)| {
 | 
			
		||||
                    let credits_earned = credits - prev_credits;
 | 
			
		||||
                    let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
 | 
			
		||||
                    (acc.0 + credits_earned, acc.1 + slots_in_epoch, acc.2 + 1)
 | 
			
		||||
                });
 | 
			
		||||
            let total_uptime = credits_earned as f64 / slots_in_epoch as f64;
 | 
			
		||||
            println!("{:.2}% over {} epochs", total_uptime * 100_f64, epochs,);
 | 
			
		||||
            let (total_credits, total_slots, epochs) =
 | 
			
		||||
                aggregate_epoch_credits(&epoch_credits, &epoch_schedule);
 | 
			
		||||
            if total_slots > 0 {
 | 
			
		||||
                let total_uptime = 100_f64 * total_credits as f64 / total_slots as f64;
 | 
			
		||||
                println!("{:.2}% over {} epochs", total_uptime, epochs);
 | 
			
		||||
            } else {
 | 
			
		||||
                println!("Insufficient voting history available");
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            for (epoch, credits, prev_credits) in epoch_credits {
 | 
			
		||||
                let credits_earned = credits - prev_credits;
 | 
			
		||||
                let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
 | 
			
		||||
                let slots_in_epoch = epoch_schedule.get_slots_in_epoch(epoch);
 | 
			
		||||
                let uptime = credits_earned as f64 / slots_in_epoch as f64;
 | 
			
		||||
                println!("- epoch: {} {:.2}% uptime", epoch, uptime * 100_f64,);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(x) = span {
 | 
			
		||||
            if x > epoch_credits_vec.len() as u64 {
 | 
			
		||||
            if x > vote_state.epoch_credits().len() as u64 {
 | 
			
		||||
                println!("(span longer than available epochs)");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -97,6 +97,10 @@ pub struct RpcVoteAccountInfo {
 | 
			
		||||
    /// Whether this account is staked for the current epoch
 | 
			
		||||
    pub epoch_vote_account: bool,
 | 
			
		||||
 | 
			
		||||
    /// History of how many credits earned by the end of each epoch
 | 
			
		||||
    ///   each tuple is (Epoch, credits, prev_credits)
 | 
			
		||||
    pub epoch_credits: Vec<(Epoch, u64, u64)>,
 | 
			
		||||
 | 
			
		||||
    /// Most recent slot voted on by this vote account (0 if no votes exist)
 | 
			
		||||
    pub last_vote: u64,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -251,6 +251,7 @@ impl JsonRpcRequestProcessor {
 | 
			
		||||
                    activated_stake: *activated_stake,
 | 
			
		||||
                    commission: vote_state.commission,
 | 
			
		||||
                    root_slot: vote_state.root_slot.unwrap_or(0),
 | 
			
		||||
                    epoch_credits: vote_state.epoch_credits().clone(),
 | 
			
		||||
                    epoch_vote_account,
 | 
			
		||||
                    last_vote,
 | 
			
		||||
                }
 | 
			
		||||
@@ -1014,7 +1015,10 @@ pub mod tests {
 | 
			
		||||
        system_transaction,
 | 
			
		||||
        transaction::TransactionError,
 | 
			
		||||
    };
 | 
			
		||||
    use solana_vote_program::{vote_instruction, vote_state::VoteInit};
 | 
			
		||||
    use solana_vote_program::{
 | 
			
		||||
        vote_instruction,
 | 
			
		||||
        vote_state::{Vote, VoteInit, MAX_LOCKOUT_HISTORY},
 | 
			
		||||
    };
 | 
			
		||||
    use std::{
 | 
			
		||||
        collections::HashMap,
 | 
			
		||||
        sync::atomic::{AtomicBool, Ordering},
 | 
			
		||||
@@ -1022,20 +1026,23 @@ pub mod tests {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const TEST_MINT_LAMPORTS: u64 = 1_000_000;
 | 
			
		||||
    const TEST_SLOTS_PER_EPOCH: u64 = 50;
 | 
			
		||||
 | 
			
		||||
    struct RpcHandler {
 | 
			
		||||
        io: MetaIoHandler<Meta>,
 | 
			
		||||
        meta: Meta,
 | 
			
		||||
        bank: Arc<Bank>,
 | 
			
		||||
        bank_forks: Arc<RwLock<BankForks>>,
 | 
			
		||||
        blockhash: Hash,
 | 
			
		||||
        alice: Keypair,
 | 
			
		||||
        leader_pubkey: Pubkey,
 | 
			
		||||
        leader_vote_keypair: Keypair,
 | 
			
		||||
        block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
 | 
			
		||||
        confirmed_block_signatures: Vec<Signature>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn start_rpc_handler_with_tx(pubkey: &Pubkey) -> RpcHandler {
 | 
			
		||||
        let (bank_forks, alice) = new_bank_forks();
 | 
			
		||||
        let (bank_forks, alice, leader_vote_keypair) = new_bank_forks();
 | 
			
		||||
        let bank = bank_forks.read().unwrap().working_bank();
 | 
			
		||||
 | 
			
		||||
        let commitment_slot0 = BlockCommitment::new([8; MAX_LOCKOUT_HISTORY]);
 | 
			
		||||
@@ -1077,7 +1084,7 @@ pub mod tests {
 | 
			
		||||
 | 
			
		||||
        let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new(
 | 
			
		||||
            JsonRpcConfig::default(),
 | 
			
		||||
            bank_forks,
 | 
			
		||||
            bank_forks.clone(),
 | 
			
		||||
            block_commitment_cache.clone(),
 | 
			
		||||
            blocktree,
 | 
			
		||||
            StorageState::default(),
 | 
			
		||||
@@ -1107,9 +1114,11 @@ pub mod tests {
 | 
			
		||||
            io,
 | 
			
		||||
            meta,
 | 
			
		||||
            bank,
 | 
			
		||||
            bank_forks,
 | 
			
		||||
            blockhash,
 | 
			
		||||
            alice,
 | 
			
		||||
            leader_pubkey,
 | 
			
		||||
            leader_vote_keypair,
 | 
			
		||||
            block_commitment_cache,
 | 
			
		||||
            confirmed_block_signatures,
 | 
			
		||||
        }
 | 
			
		||||
@@ -1120,7 +1129,7 @@ pub mod tests {
 | 
			
		||||
        let bob_pubkey = Pubkey::new_rand();
 | 
			
		||||
        let exit = Arc::new(AtomicBool::new(false));
 | 
			
		||||
        let validator_exit = create_validator_exit(&exit);
 | 
			
		||||
        let (bank_forks, alice) = new_bank_forks();
 | 
			
		||||
        let (bank_forks, alice, _) = new_bank_forks();
 | 
			
		||||
        let bank = bank_forks.read().unwrap().working_bank();
 | 
			
		||||
        let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
 | 
			
		||||
        let ledger_path = get_tmp_ledger_path!();
 | 
			
		||||
@@ -1630,20 +1639,23 @@ pub mod tests {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn new_bank_forks() -> (Arc<RwLock<BankForks>>, Keypair) {
 | 
			
		||||
    fn new_bank_forks() -> (Arc<RwLock<BankForks>>, Keypair, Keypair) {
 | 
			
		||||
        let GenesisConfigInfo {
 | 
			
		||||
            mut genesis_config,
 | 
			
		||||
            mint_keypair,
 | 
			
		||||
            ..
 | 
			
		||||
            voting_keypair,
 | 
			
		||||
        } = create_genesis_config(TEST_MINT_LAMPORTS);
 | 
			
		||||
 | 
			
		||||
        genesis_config.rent.lamports_per_byte_year = 50;
 | 
			
		||||
        genesis_config.rent.exemption_threshold = 2.0;
 | 
			
		||||
        genesis_config.epoch_schedule =
 | 
			
		||||
            EpochSchedule::custom(TEST_SLOTS_PER_EPOCH, TEST_SLOTS_PER_EPOCH, false);
 | 
			
		||||
 | 
			
		||||
        let bank = Bank::new(&genesis_config);
 | 
			
		||||
        (
 | 
			
		||||
            Arc::new(RwLock::new(BankForks::new(bank.slot(), bank))),
 | 
			
		||||
            mint_keypair,
 | 
			
		||||
            voting_keypair,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1905,8 +1917,10 @@ pub mod tests {
 | 
			
		||||
        let RpcHandler {
 | 
			
		||||
            io,
 | 
			
		||||
            meta,
 | 
			
		||||
            bank,
 | 
			
		||||
            mut bank,
 | 
			
		||||
            bank_forks,
 | 
			
		||||
            alice,
 | 
			
		||||
            leader_vote_keypair,
 | 
			
		||||
            ..
 | 
			
		||||
        } = start_rpc_handler_with_tx(&Pubkey::new_rand());
 | 
			
		||||
 | 
			
		||||
@@ -1936,7 +1950,41 @@ pub mod tests {
 | 
			
		||||
            .expect("process transaction");
 | 
			
		||||
        assert_eq!(bank.vote_accounts().len(), 2);
 | 
			
		||||
 | 
			
		||||
        let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getVoteAccounts"}}"#);
 | 
			
		||||
        // Advance bank to the next epoch
 | 
			
		||||
        for _ in 0..TEST_SLOTS_PER_EPOCH {
 | 
			
		||||
            bank.freeze();
 | 
			
		||||
 | 
			
		||||
            let instruction = vote_instruction::vote(
 | 
			
		||||
                &leader_vote_keypair.pubkey(),
 | 
			
		||||
                &leader_vote_keypair.pubkey(),
 | 
			
		||||
                Vote {
 | 
			
		||||
                    slots: vec![bank.slot()],
 | 
			
		||||
                    hash: bank.hash(),
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            bank = bank_forks.write().unwrap().insert(Bank::new_from_parent(
 | 
			
		||||
                &bank,
 | 
			
		||||
                &Pubkey::default(),
 | 
			
		||||
                bank.slot() + 1,
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
            let transaction = Transaction::new_signed_with_payer(
 | 
			
		||||
                vec![instruction],
 | 
			
		||||
                Some(&alice.pubkey()),
 | 
			
		||||
                &[&alice, &leader_vote_keypair],
 | 
			
		||||
                bank.last_blockhash(),
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            bank.process_transaction(&transaction)
 | 
			
		||||
                .expect("process transaction");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let req = format!(
 | 
			
		||||
            r#"{{"jsonrpc":"2.0","id":1,"method":"getVoteAccounts","params":{}}}"#,
 | 
			
		||||
            json!([CommitmentConfig::recent()])
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let res = io.handle_request_sync(&req, meta.clone());
 | 
			
		||||
        let result: Value = serde_json::from_str(&res.expect("actual response"))
 | 
			
		||||
            .expect("actual response deserialization");
 | 
			
		||||
@@ -1944,12 +1992,19 @@ pub mod tests {
 | 
			
		||||
        let vote_account_status: RpcVoteAccountStatus =
 | 
			
		||||
            serde_json::from_value(result["result"].clone()).unwrap();
 | 
			
		||||
 | 
			
		||||
        // The bootstrap leader vote account will be delinquent as it has stake but has never
 | 
			
		||||
        // voted.  The vote account with no stake should not be present.
 | 
			
		||||
        assert!(vote_account_status.current.is_empty());
 | 
			
		||||
        assert_eq!(vote_account_status.delinquent.len(), 1);
 | 
			
		||||
        for vote_account_info in vote_account_status.delinquent {
 | 
			
		||||
            assert_ne!(vote_account_info.activated_stake, 0);
 | 
			
		||||
        }
 | 
			
		||||
        // The vote account with no stake should not be present.
 | 
			
		||||
        assert!(vote_account_status.delinquent.is_empty());
 | 
			
		||||
 | 
			
		||||
        // The leader vote account should be active and have voting history.
 | 
			
		||||
        assert_eq!(vote_account_status.current.len(), 1);
 | 
			
		||||
        let leader_info = &vote_account_status.current[0];
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            leader_info.vote_pubkey,
 | 
			
		||||
            leader_vote_keypair.pubkey().to_string()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(leader_info.activated_stake, 0);
 | 
			
		||||
        // Subtract one because the last vote always carries over to the next epoch
 | 
			
		||||
        let expected_credits = TEST_SLOTS_PER_EPOCH - MAX_LOCKOUT_HISTORY as u64 - 1;
 | 
			
		||||
        assert_eq!(leader_info.epoch_credits, vec![(0, expected_credits, 0)]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -315,8 +315,8 @@ impl VoteState {
 | 
			
		||||
    /// Each tuple of (Epoch, u64, u64) is read as (epoch, credits, prev_credits), where
 | 
			
		||||
    ///   credits for each epoch is credits - prev_credits; while redundant this makes
 | 
			
		||||
    ///   calculating rewards over partial epochs nice and simple
 | 
			
		||||
    pub fn epoch_credits(&self) -> impl Iterator<Item = &(Epoch, u64, u64)> {
 | 
			
		||||
        self.epoch_credits.iter()
 | 
			
		||||
    pub fn epoch_credits(&self) -> &Vec<(Epoch, u64, u64)> {
 | 
			
		||||
        &self.epoch_credits
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn pop_expired_votes(&mut self, slot: Slot) {
 | 
			
		||||
@@ -1194,13 +1194,7 @@ mod tests {
 | 
			
		||||
        let mut vote_state = VoteState::default();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(vote_state.credits(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            vote_state
 | 
			
		||||
                .epoch_credits()
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .collect::<Vec<(Epoch, u64, u64)>>(),
 | 
			
		||||
            vec![]
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(vote_state.epoch_credits().clone(), vec![]);
 | 
			
		||||
 | 
			
		||||
        let mut expected = vec![];
 | 
			
		||||
        let mut credits = 0;
 | 
			
		||||
@@ -1218,45 +1212,18 @@ mod tests {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assert_eq!(vote_state.credits(), credits);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            vote_state
 | 
			
		||||
                .epoch_credits()
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .collect::<Vec<(Epoch, u64, u64)>>(),
 | 
			
		||||
            expected
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(vote_state.epoch_credits().clone(), expected);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_vote_state_epoch0_no_credits() {
 | 
			
		||||
        let mut vote_state = VoteState::default();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            vote_state
 | 
			
		||||
                .epoch_credits()
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .collect::<Vec<(Epoch, u64, u64)>>()
 | 
			
		||||
                .len(),
 | 
			
		||||
            0
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(vote_state.epoch_credits().len(), 0);
 | 
			
		||||
        vote_state.increment_credits(1);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            vote_state
 | 
			
		||||
                .epoch_credits()
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .collect::<Vec<(Epoch, u64, u64)>>()
 | 
			
		||||
                .len(),
 | 
			
		||||
            0
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(vote_state.epoch_credits().len(), 0);
 | 
			
		||||
 | 
			
		||||
        vote_state.increment_credits(2);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            vote_state
 | 
			
		||||
                .epoch_credits()
 | 
			
		||||
                .cloned()
 | 
			
		||||
                .collect::<Vec<(Epoch, u64, u64)>>()
 | 
			
		||||
                .len(),
 | 
			
		||||
            1
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(vote_state.epoch_credits().len(), 1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user