moar coverage in stake_state (#5554)

* moar coverage in stake_state

* nits
This commit is contained in:
Rob Walker
2019-08-18 15:41:49 -07:00
committed by GitHub
parent 814af378a7
commit 626e16a177

View File

@ -73,23 +73,15 @@ impl Default for Stake {
} }
impl Stake { impl Stake {
pub fn is_bootstrap(&self) -> bool { fn is_bootstrap(&self) -> bool {
self.activation_epoch == std::u64::MAX self.activation_epoch == std::u64::MAX
} }
pub fn activating(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
self.stake_activating_and_deactivating(epoch, history).1
}
pub fn deactivating(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
self.stake_activating_and_deactivating(epoch, history).2
}
pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 { pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
self.stake_activating_and_deactivating(epoch, history).0 self.stake_activating_and_deactivating(epoch, history).0
} }
pub fn stake_activating_and_deactivating( fn stake_activating_and_deactivating(
&self, &self,
epoch: Epoch, epoch: Epoch,
history: Option<&StakeHistory>, history: Option<&StakeHistory>,
@ -205,7 +197,7 @@ impl Stake {
/// * staker_rewards to be distributed /// * staker_rewards to be distributed
/// * new value for credits_observed in the stake /// * new value for credits_observed in the stake
// returns None if there's no payout or if any deserved payout is < 1 lamport // returns None if there's no payout or if any deserved payout is < 1 lamport
pub fn calculate_rewards( fn calculate_rewards(
&self, &self,
point_value: f64, point_value: f64,
vote_state: &VoteState, vote_state: &VoteState,
@ -258,13 +250,13 @@ impl Stake {
} }
fn new_bootstrap(stake: u64, voter_pubkey: &Pubkey, vote_state: &VoteState) -> Self { fn new_bootstrap(stake: u64, voter_pubkey: &Pubkey, vote_state: &VoteState) -> Self {
Self { Self::new(
stake, stake,
activation_epoch: std::u64::MAX, voter_pubkey,
voter_pubkey: *voter_pubkey, vote_state,
credits_observed: vote_state.credits(), std::u64::MAX,
..Stake::default() &Config::default(),
} )
} }
fn new( fn new(
@ -502,33 +494,35 @@ mod tests {
use solana_sdk::system_program; use solana_sdk::system_program;
use solana_vote_api::vote_state; use solana_vote_api::vote_state;
fn create_stake_history_from_stakes( #[test]
bootstrap: Option<u64>, fn test_stake_state_stake_from_fail() {
epochs: std::ops::Range<Epoch>, let mut stake_account = Account::new(0, std::mem::size_of::<StakeState>(), &id());
stakes: &[Stake],
) -> StakeHistory {
let mut stake_history = StakeHistory::default();
let bootstrap_stake = if let Some(bootstrap) = bootstrap { stake_account
vec![Stake { .set_state(&StakeState::default())
.expect("set_state");
assert_eq!(StakeState::stake_from(&stake_account), None);
}
#[test]
fn test_stake_is_bootstrap() {
assert_eq!(
Stake {
activation_epoch: std::u64::MAX, activation_epoch: std::u64::MAX,
stake: bootstrap,
..Stake::default() ..Stake::default()
}] }
} else { .is_bootstrap(),
vec![] true
}; );
assert_eq!(
for epoch in epochs { Stake {
let entry = new_stake_history_entry( activation_epoch: 0,
epoch, ..Stake::default()
stakes.iter().chain(bootstrap_stake.iter()), }
Some(&stake_history), .is_bootstrap(),
); false
stake_history.add(epoch, entry); );
}
stake_history
} }
#[test] #[test]
@ -624,8 +618,172 @@ mod tests {
.is_err()); .is_err());
} }
fn create_stake_history_from_stakes(
bootstrap: Option<u64>,
epochs: std::ops::Range<Epoch>,
stakes: &[Stake],
) -> StakeHistory {
let mut stake_history = StakeHistory::default();
let bootstrap_stake = if let Some(bootstrap) = bootstrap {
vec![Stake {
activation_epoch: std::u64::MAX,
stake: bootstrap,
..Stake::default()
}]
} else {
vec![]
};
for epoch in epochs {
let entry = new_stake_history_entry(
epoch,
stakes.iter().chain(bootstrap_stake.iter()),
Some(&stake_history),
);
stake_history.add(epoch, entry);
}
stake_history
}
#[test] #[test]
fn test_stake_warmup() { fn test_stake_activating_and_deactivating() {
let stake = Stake {
stake: 1_000,
activation_epoch: 0, // activating at zero
deactivation_epoch: 5,
..Stake::default()
};
// save this off so stake.config.warmup_rate changes don't break this test
let increment = (1_000 as f64 * stake.config.warmup_rate) as u64;
let mut stake_history = StakeHistory::default();
// assert that this stake follows step function if there's no history
assert_eq!(
stake.stake_activating_and_deactivating(stake.activation_epoch, Some(&stake_history)),
(0, stake.stake, 0)
);
for epoch in stake.activation_epoch + 1..stake.deactivation_epoch {
assert_eq!(
stake.stake_activating_and_deactivating(epoch, Some(&stake_history)),
(stake.stake, 0, 0)
);
}
// assert that this stake is full deactivating
assert_eq!(
stake.stake_activating_and_deactivating(stake.deactivation_epoch, Some(&stake_history)),
(stake.stake, 0, stake.stake)
);
// assert that this stake is fully deactivated if there's no history
assert_eq!(
stake.stake_activating_and_deactivating(
stake.deactivation_epoch + 1,
Some(&stake_history)
),
(0, 0, 0)
);
stake_history.add(
0u64, // entry for zero doesn't have my activating amount
StakeHistoryEntry {
effective: 1_000,
activating: 0,
..StakeHistoryEntry::default()
},
);
// assert that this stake is broken, because above setup is broken
assert_eq!(
stake.stake_activating_and_deactivating(1, Some(&stake_history)),
(0, stake.stake, 0)
);
stake_history.add(
0u64, // entry for zero has my activating amount
StakeHistoryEntry {
effective: 1_000,
activating: 1_000,
..StakeHistoryEntry::default()
},
// no entry for 1, so this stake gets shorted
);
// assert that this stake is broken, because above setup is broken
assert_eq!(
stake.stake_activating_and_deactivating(2, Some(&stake_history)),
(increment, stake.stake - increment, 0)
);
// start over, test deactivation edge cases
let mut stake_history = StakeHistory::default();
stake_history.add(
stake.deactivation_epoch, // entry for zero doesn't have my de-activating amount
StakeHistoryEntry {
effective: 1_000,
activating: 0,
..StakeHistoryEntry::default()
},
);
// assert that this stake is broken, because above setup is broken
assert_eq!(
stake.stake_activating_and_deactivating(
stake.deactivation_epoch + 1,
Some(&stake_history)
),
(stake.stake, 0, stake.stake) // says "I'm still waiting for deactivation"
);
// put in my initial deactivating amount, but don't put in an entry for next
stake_history.add(
stake.deactivation_epoch, // entry for zero has my de-activating amount
StakeHistoryEntry {
effective: 1_000,
deactivating: 1_000,
..StakeHistoryEntry::default()
},
);
// assert that this stake is broken, because above setup is broken
assert_eq!(
stake.stake_activating_and_deactivating(
stake.deactivation_epoch + 2,
Some(&stake_history)
),
(stake.stake - increment, 0, stake.stake - increment) // hung, should be lower
);
}
#[test]
fn test_stake_warmup_cooldown_sub_integer_moves() {
let stakes = [Stake {
stake: 2,
activation_epoch: 0, // activating at zero
deactivation_epoch: 5,
..Stake::default()
}];
// give 2 epochs of cooldown
let epochs = 7;
// make boostrap stake smaller than warmup so warmup/cooldownn
// increment is always smaller than 1
let bootstrap = (stakes[0].config.warmup_rate * 100.0 / 2.0) as u64;
let stake_history = create_stake_history_from_stakes(Some(bootstrap), 0..epochs, &stakes);
let mut max_stake = 0;
let mut min_stake = 2;
for epoch in 0..epochs {
let stake = stakes
.iter()
.map(|stake| stake.stake(epoch, Some(&stake_history)))
.sum::<u64>();
max_stake = max_stake.max(stake);
min_stake = min_stake.min(stake);
}
assert_eq!(max_stake, 2);
assert_eq!(min_stake, 0);
}
#[test]
fn test_stake_warmup_cooldown() {
let stakes = [ let stakes = [
Stake { Stake {
// never deactivates // never deactivates