Implement locktower voting (#3251)

* locktower components and tests

* integrate locktower into replay stage

* track locktower duration

* make sure threshold is checked after simulating the vote

* check vote lockouts using the VoteState program

* duplicate vote test

* epoch stakes

* disable impossible to verify tests
This commit is contained in:
anatoly yakovenko
2019-03-18 12:12:33 -07:00
committed by GitHub
parent cedff2fca1
commit 61a4b998fa
7 changed files with 735 additions and 54 deletions

View File

@ -39,6 +39,9 @@ impl Lockout {
pub fn expiration_slot(&self) -> u64 {
self.slot + self.lockout()
}
pub fn is_expired(&self, slot: u64) -> bool {
self.expiration_slot() < slot
}
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
@ -110,6 +113,15 @@ impl VoteState {
self.double_lockouts();
}
pub fn nth_recent_vote(&self, position: usize) -> Option<&Lockout> {
if position < self.votes.len() {
let pos = self.votes.len() - 1 - position;
self.votes.get(pos)
} else {
None
}
}
/// Number of "credits" owed to this account from the mining pool. Submit this
/// VoteState to the Rewards program to trade credits for lamports.
pub fn credits(&self) -> u64 {
@ -123,11 +135,7 @@ impl VoteState {
fn pop_expired_votes(&mut self, slot: u64) {
loop {
if self
.votes
.back()
.map_or(false, |v| v.expiration_slot() < slot)
{
if self.votes.back().map_or(false, |v| v.is_expired(slot)) {
self.votes.pop_back();
} else {
break;
@ -462,6 +470,34 @@ mod tests {
assert_eq!(vote_state.credits(), 0);
}
#[test]
fn test_duplicate_vote() {
let voter_id = Keypair::new().pubkey();
let mut vote_state = VoteState::new(&voter_id);
vote_state.process_vote(Vote::new(0));
vote_state.process_vote(Vote::new(1));
vote_state.process_vote(Vote::new(0));
assert_eq!(vote_state.nth_recent_vote(0).unwrap().slot, 1);
assert_eq!(vote_state.nth_recent_vote(1).unwrap().slot, 0);
assert!(vote_state.nth_recent_vote(2).is_none());
}
#[test]
fn test_nth_recent_vote() {
let voter_id = Keypair::new().pubkey();
let mut vote_state = VoteState::new(&voter_id);
for i in 0..MAX_LOCKOUT_HISTORY {
vote_state.process_vote(Vote::new(i as u64));
}
for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
assert_eq!(
vote_state.nth_recent_vote(i).unwrap().slot as usize,
MAX_LOCKOUT_HISTORY - i - 1,
);
}
assert!(vote_state.nth_recent_vote(MAX_LOCKOUT_HISTORY).is_none());
}
fn check_lockouts(vote_state: &VoteState) {
for (i, vote) in vote_state.votes.iter().enumerate() {
let num_lockouts = vote_state.votes.len() - i;