From 549f9676f1e144d17927121f84ef9382ea3239ad Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 5 Feb 2019 10:25:59 -0700 Subject: [PATCH] Allow validators to accumulate credits for voting --- programs/native/vote/src/lib.rs | 14 ++------- sdk/src/vote_program.rs | 53 +++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/programs/native/vote/src/lib.rs b/programs/native/vote/src/lib.rs index 78c3d0e55..137fb8af0 100644 --- a/programs/native/vote/src/lib.rs +++ b/programs/native/vote/src/lib.rs @@ -39,19 +39,8 @@ fn process_vote(keyed_accounts: &mut [KeyedAccount], vote: Vote) -> Result<(), P } let mut vote_state = VoteState::deserialize(&keyed_accounts[0].account.userdata)?; - - // TODO: Integrity checks - // a) Verify the vote's bank hash matches what is expected - // b) Verify vote is older than previous votes - - // Only keep around the most recent MAX_VOTE_HISTORY votes - if vote_state.votes.len() == vote_program::MAX_VOTE_HISTORY { - vote_state.votes.pop_front(); - } - - vote_state.votes.push_back(vote); + vote_state.process_vote(vote); vote_state.serialize(&mut keyed_accounts[0].account.userdata)?; - Ok(()) } @@ -152,6 +141,7 @@ mod tests { let vote = Vote::new(1); let vote_state = vote_and_deserialize(&vote_id, &mut vote_account, vote.clone()).unwrap(); assert_eq!(vote_state.votes, vec![vote]); + assert_eq!(vote_state.credits(), 0); } #[test] diff --git a/sdk/src/vote_program.rs b/sdk/src/vote_program.rs index 2c2793cc9..3aa16e1c0 100644 --- a/sdk/src/vote_program.rs +++ b/sdk/src/vote_program.rs @@ -20,7 +20,7 @@ pub fn id() -> Pubkey { } // Maximum number of votes to keep around -pub const MAX_VOTE_HISTORY: usize = 32; +const MAX_VOTE_HISTORY: usize = 32; #[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Vote { @@ -51,6 +51,7 @@ pub struct VoteState { pub votes: VecDeque, pub node_id: Pubkey, pub staker_id: Pubkey, + credits: u64, } pub fn get_max_size() -> usize { @@ -64,10 +65,12 @@ pub fn get_max_size() -> usize { impl VoteState { pub fn new(node_id: Pubkey, staker_id: Pubkey) -> Self { let votes = VecDeque::new(); + let credits = 0; Self { votes, node_id, staker_id, + credits, } } @@ -81,6 +84,31 @@ impl VoteState { _ => ProgramError::GenericError, }) } + + pub fn process_vote(&mut self, vote: Vote) { + // TODO: Integrity checks + // a) Verify the vote's bank hash matches what is expected + // b) Verify vote is older than previous votes + + // Only keep around the most recent MAX_VOTE_HISTORY votes + if self.votes.len() == MAX_VOTE_HISTORY { + self.votes.pop_front(); + self.credits += 1; + } + + self.votes.push_back(vote); + } + + /// 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 { + self.credits + } + + /// Clear any credits. + pub fn clear_credits(&mut self) { + self.credits = 0; + } } #[cfg(test)] @@ -88,11 +116,24 @@ mod tests { use super::*; #[test] - fn test_serde() { + fn test_vote_serialize() { let mut buffer: Vec = vec![0; get_max_size()]; - let mut vote_program = VoteState::default(); - vote_program.votes = (0..MAX_VOTE_HISTORY).map(|_| Vote::default()).collect(); - vote_program.serialize(&mut buffer).unwrap(); - assert_eq!(VoteState::deserialize(&buffer).unwrap(), vote_program); + let mut vote_state = VoteState::default(); + vote_state.votes.resize(MAX_VOTE_HISTORY, Vote::default()); + vote_state.serialize(&mut buffer).unwrap(); + assert_eq!(VoteState::deserialize(&buffer).unwrap(), vote_state); + } + + #[test] + fn test_vote_credits() { + let mut vote_state = VoteState::default(); + vote_state.votes.resize(MAX_VOTE_HISTORY, Vote::default()); + assert_eq!(vote_state.credits(), 0); + vote_state.process_vote(Vote::new(42)); + assert_eq!(vote_state.credits(), 1); + vote_state.process_vote(Vote::new(43)); + assert_eq!(vote_state.credits(), 2); + vote_state.clear_credits(); + assert_eq!(vote_state.credits(), 0); } }