Fix update_ancestor_stakes in locktower (#3631)

* Fix update_ancestor_stakes in locktower

* Add test for vote threshold
This commit is contained in:
carllin 2019-04-05 03:05:31 -07:00 committed by GitHub
parent 9cd555cad5
commit 4ea19b90a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 69 additions and 2 deletions

View File

@ -162,8 +162,24 @@ impl Locktower {
};
Self::update_ancestor_lockouts(&mut stake_lockouts, &vote, ancestors);
}
// each account hash a stake for all the forks in the active tree for this bank
Self::update_ancestor_stakes(&mut stake_lockouts, bank_slot, lamports, ancestors);
// The last vote in the vote stack is a simulated vote on bank_slot, which
// we added to the vote stack earlier in this function by calling process_vote().
// We don't want to update the ancestors stakes of this vote b/c it does not
// represent an actual vote by the validator.
// Note: It should not be possible for any vote state in this bank to have
// a vote for a slot >= bank_slot, so we are guaranteed that the last vote in
// this vote stack is the simulated vote, so this fetch should be sufficient
// to find the last unsimulated vote.
assert_eq!(
vote_state.nth_recent_vote(0).map(|l| l.slot),
Some(bank_slot)
);
if let Some(vote) = vote_state.nth_recent_vote(1) {
// Update all the parents of this last vote with the stake of this vote account
Self::update_ancestor_stakes(&mut stake_lockouts, vote.slot, lamports, ancestors);
}
}
stake_lockouts
}
@ -728,4 +744,55 @@ mod test {
assert_eq!(stake_lockouts[&1].stake, 1);
assert_eq!(stake_lockouts[&2].stake, 1);
}
#[test]
fn test_check_vote_threshold_forks() {
// Create the ancestor relationships
let ancestors = (0..=(VOTE_THRESHOLD_DEPTH + 1) as u64)
.map(|slot| {
let slot_parents: HashSet<_> = (0..slot).collect();
(slot, slot_parents)
})
.collect();
// Create votes such that
// 1) 3/4 of the stake has voted on slot: VOTE_THRESHOLD_DEPTH - 2, lockout: 2
// 2) 1/4 of the stake has voted on slot: VOTE_THRESHOLD_DEPTH, lockout: 2^9
let total_stake = 4;
let threshold_size = 0.67;
let threshold_stake = (f64::ceil(total_stake as f64 * threshold_size)) as u64;
let locktower_votes: Vec<u64> = (0..VOTE_THRESHOLD_DEPTH as u64).collect();
let accounts = gen_accounts(&[
(threshold_stake, &[(VOTE_THRESHOLD_DEPTH - 2) as u64]),
(total_stake - threshold_stake, &locktower_votes[..]),
]);
// Initialize locktower
let stakes: HashMap<_, _> = accounts.iter().map(|(pk, a)| (*pk, a.lamports)).collect();
let epoch_stakes = EpochStakes::new(0, stakes, &Pubkey::default());
let mut locktower = Locktower::new(epoch_stakes, VOTE_THRESHOLD_DEPTH, threshold_size);
// CASE 1: Record the first VOTE_THRESHOLD locktower votes for fork 2. We want to
// evaluate a vote on slot VOTE_THRESHOLD_DEPTH. The nth most recent vote should be
// for slot 0, which is common to all account vote states, so we should pass the
// threshold check
let vote_to_evaluate = VOTE_THRESHOLD_DEPTH as u64;
for vote in &locktower_votes {
locktower.record_vote(*vote);
}
let stakes_lockouts = locktower.collect_vote_lockouts(
vote_to_evaluate,
accounts.clone().into_iter(),
&ancestors,
);
assert!(locktower.check_vote_stake_threshold(vote_to_evaluate, &stakes_lockouts));
// CASE 2: Now we want to evaluate a vote for slot VOTE_THRESHOLD_DEPTH + 1. This slot
// will expire the vote in one of the vote accounts, so we should have insufficient
// stake to pass the threshold
let vote_to_evaluate = VOTE_THRESHOLD_DEPTH as u64 + 1;
let stakes_lockouts =
locktower.collect_vote_lockouts(vote_to_evaluate, accounts.into_iter(), &ancestors);
assert!(!locktower.check_vote_stake_threshold(vote_to_evaluate, &stakes_lockouts));
}
}