Fix update_ancestor_stakes in locktower (#3631)
* Fix update_ancestor_stakes in locktower * Add test for vote threshold
This commit is contained in:
parent
9cd555cad5
commit
4ea19b90a4
|
@ -162,8 +162,24 @@ impl Locktower {
|
||||||
};
|
};
|
||||||
Self::update_ancestor_lockouts(&mut stake_lockouts, &vote, ancestors);
|
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
|
stake_lockouts
|
||||||
}
|
}
|
||||||
|
@ -728,4 +744,55 @@ mod test {
|
||||||
assert_eq!(stake_lockouts[&1].stake, 1);
|
assert_eq!(stake_lockouts[&1].stake, 1);
|
||||||
assert_eq!(stake_lockouts[&2].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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue