diff --git a/Cargo.lock b/Cargo.lock index a7ae4daf9c..54eb0bb0cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2213,6 +2213,7 @@ dependencies = [ "solana-logger 0.12.0", "solana-metrics 0.12.0", "solana-sdk 0.12.0", + "solana-vote-program 0.12.0", ] [[package]] diff --git a/programs/native/rewards/Cargo.toml b/programs/native/rewards/Cargo.toml index c8f53f2457..c3e5c29789 100644 --- a/programs/native/rewards/Cargo.toml +++ b/programs/native/rewards/Cargo.toml @@ -17,6 +17,9 @@ solana-logger = { path = "../../../logger", version = "0.12.0" } solana-metrics = { path = "../../../metrics", version = "0.12.0" } solana-sdk = { path = "../../../sdk", version = "0.12.0" } +[dev-dependencies] +solana-vote-program = { path = "../vote", version = "0.12.0" } + [lib] name = "solana_rewards_program" crate-type = ["cdylib"] diff --git a/programs/native/rewards/src/lib.rs b/programs/native/rewards/src/lib.rs index 472e1f703e..3fd9d0fa3f 100644 --- a/programs/native/rewards/src/lib.rs +++ b/programs/native/rewards/src/lib.rs @@ -63,6 +63,8 @@ fn redeem_vote_credits(keyed_accounts: &mut [KeyedAccount]) -> Result<(), Progra keyed_accounts[0].account.tokens -= lamports; keyed_accounts[2].account.tokens += lamports; + // TODO: The runtime should reject this, because this program + // is not the owner of the VoteState account. vote_state.clear_credits(); vote_state.serialize(&mut keyed_accounts[1].account.userdata)?; @@ -91,65 +93,89 @@ fn entrypoint( } } -//#[cfg(test)] -//mod tests { -// use super::*; -// use solana_sdk::account::Account; -// use solana_sdk::signature::{Keypair, KeypairUtil}; -// use solana_sdk::vote_program; -// -// fn create_vote_account(tokens: u64) -> Account { -// let space = vote_program::get_max_size(); -// Account::new(tokens, space, vote_program::id()) -// } -// -// fn register_and_deserialize( -// from_id: &Pubkey, -// from_account: &mut Account, -// vote_id: &Pubkey, -// vote_account: &mut Account, -// ) -> Result { -// let mut keyed_accounts = [ -// KeyedAccount::new(from_id, true, from_account), -// KeyedAccount::new(vote_id, false, vote_account), -// ]; -// register(&mut keyed_accounts)?; -// let vote_state = VoteState::deserialize(&vote_account.userdata).unwrap(); -// Ok(vote_state) -// } -// -// fn vote_and_deserialize( -// vote_id: &Pubkey, -// vote_account: &mut Account, -// vote: Vote, -// ) -> Result { -// let mut keyed_accounts = [KeyedAccount::new(vote_id, true, vote_account)]; -// process_vote(&mut keyed_accounts, vote)?; -// let vote_state = VoteState::deserialize(&vote_account.userdata).unwrap(); -// Ok(vote_state) -// } -// -// #[test] -// fn test_redeem_vote_credits() { -// let from_id = Keypair::new().pubkey(); -// let mut from_account = Account::new(100, 0, Pubkey::default()); -// -// let vote_id = Keypair::new().pubkey(); -// let mut vote_account = create_vote_account(100); -// register_and_deserialize(&from_id, &mut from_account, &vote_id, &mut vote_account).unwrap(); -// -// for _ in 0..vote_program::MAX_VOTE_HISTORY { -// let vote = Vote::new(1); -// let vote_state = -// vote_and_deserialize(&vote_id, &mut vote_account, vote.clone()).unwrap(); -// assert_eq!(vote_state.credits(), 0); -// } -// -// let vote = Vote::new(1); -// let vote_state = vote_and_deserialize(&vote_id, &mut vote_account, vote.clone()).unwrap(); -// assert_eq!(vote_state.credits(), 1); -// -// let vote_state = redeem_vote_credits_and_deserialize() -// assert_eq!(vote_state.credits(), 0); -// } -//} +#[cfg(test)] +mod tests { + use super::*; + use solana_sdk::account::Account; + use solana_sdk::rewards_program; + use solana_sdk::signature::{Keypair, KeypairUtil}; + use solana_sdk::vote_program::{self, Vote}; + use solana_vote_program; + + fn create_rewards_account(tokens: u64) -> Account { + let space = rewards_program::get_max_size(); + Account::new(tokens, space, rewards_program::id()) + } + + fn redeem_vote_credits_and_deserialize( + rewards_id: &Pubkey, + rewards_account: &mut Account, + vote_id: &Pubkey, + vote_account: &mut Account, + to_id: &Pubkey, + to_account: &mut Account, + ) -> Result { + let mut keyed_accounts = [ + KeyedAccount::new(rewards_id, true, rewards_account), + KeyedAccount::new(vote_id, true, vote_account), + KeyedAccount::new(to_id, false, to_account), + ]; + redeem_vote_credits(&mut keyed_accounts)?; + let vote_state = VoteState::deserialize(&vote_account.userdata).unwrap(); + Ok(vote_state) + } + + #[test] + fn test_redeem_vote_credits() { + let from_id = Keypair::new().pubkey(); + let mut from_account = Account::new(100, 0, Pubkey::default()); + + let vote_id = Keypair::new().pubkey(); + let mut vote_account = solana_vote_program::create_vote_account(100); + solana_vote_program::register_and_deserialize( + &from_id, + &mut from_account, + &vote_id, + &mut vote_account, + ) + .unwrap(); + + for _ in 0..vote_program::MAX_VOTE_HISTORY { + let vote = Vote::new(1); + let vote_state = solana_vote_program::vote_and_deserialize( + &vote_id, + &mut vote_account, + vote.clone(), + ) + .unwrap(); + assert_eq!(vote_state.credits(), 0); + } + + let vote = Vote::new(1); + let vote_state = + solana_vote_program::vote_and_deserialize(&vote_id, &mut vote_account, vote.clone()) + .unwrap(); + assert_eq!(vote_state.credits(), 1); + + let rewards_id = Keypair::new().pubkey(); + let mut rewards_account = create_rewards_account(100); + + // TODO: Add VoteInstruction::RegisterStakerId so that we don't need to point the "to" + // account to the "from" account. + let to_id = from_id; + let mut to_account = from_account; + let to_tokens = to_account.tokens; + + let vote_state = redeem_vote_credits_and_deserialize( + &rewards_id, + &mut rewards_account, + &vote_id, + &mut vote_account, + &to_id, + &mut to_account, + ) + .unwrap(); + assert_eq!(vote_state.credits(), 0); + assert!(to_account.tokens > to_tokens); + } +} diff --git a/programs/native/vote/Cargo.toml b/programs/native/vote/Cargo.toml index b228ae6f24..3e460e2ada 100644 --- a/programs/native/vote/Cargo.toml +++ b/programs/native/vote/Cargo.toml @@ -22,5 +22,5 @@ solana-runtime = { path = "../../../runtime", version = "0.12.0" } [lib] name = "solana_vote_program" -crate-type = ["cdylib"] +crate-type = ["cdylib", "lib"] diff --git a/programs/native/vote/src/lib.rs b/programs/native/vote/src/lib.rs index 137fb8af0d..27f707bcd8 100644 --- a/programs/native/vote/src/lib.rs +++ b/programs/native/vote/src/lib.rs @@ -3,7 +3,7 @@ use bincode::deserialize; use log::*; -use solana_sdk::account::KeyedAccount; +use solana_sdk::account::{Account, KeyedAccount}; use solana_sdk::native_program::ProgramError; use solana_sdk::pubkey::Pubkey; use solana_sdk::solana_entrypoint; @@ -76,43 +76,41 @@ fn entrypoint( } } +pub fn create_vote_account(tokens: u64) -> Account { + let space = vote_program::get_max_size(); + Account::new(tokens, space, vote_program::id()) +} + +pub fn register_and_deserialize( + from_id: &Pubkey, + from_account: &mut Account, + vote_id: &Pubkey, + vote_account: &mut Account, +) -> Result { + let mut keyed_accounts = [ + KeyedAccount::new(from_id, true, from_account), + KeyedAccount::new(vote_id, false, vote_account), + ]; + register(&mut keyed_accounts)?; + let vote_state = VoteState::deserialize(&vote_account.userdata).unwrap(); + Ok(vote_state) +} + +pub fn vote_and_deserialize( + vote_id: &Pubkey, + vote_account: &mut Account, + vote: Vote, +) -> Result { + let mut keyed_accounts = [KeyedAccount::new(vote_id, true, vote_account)]; + process_vote(&mut keyed_accounts, vote)?; + let vote_state = VoteState::deserialize(&vote_account.userdata).unwrap(); + Ok(vote_state) +} + #[cfg(test)] mod tests { use super::*; - use solana_sdk::account::Account; use solana_sdk::signature::{Keypair, KeypairUtil}; - use solana_sdk::vote_program; - - fn create_vote_account(tokens: u64) -> Account { - let space = vote_program::get_max_size(); - Account::new(tokens, space, vote_program::id()) - } - - fn register_and_deserialize( - from_id: &Pubkey, - from_account: &mut Account, - vote_id: &Pubkey, - vote_account: &mut Account, - ) -> Result { - let mut keyed_accounts = [ - KeyedAccount::new(from_id, true, from_account), - KeyedAccount::new(vote_id, false, vote_account), - ]; - register(&mut keyed_accounts)?; - let vote_state = VoteState::deserialize(&vote_account.userdata).unwrap(); - Ok(vote_state) - } - - fn vote_and_deserialize( - vote_id: &Pubkey, - vote_account: &mut Account, - vote: Vote, - ) -> Result { - let mut keyed_accounts = [KeyedAccount::new(vote_id, true, vote_account)]; - process_vote(&mut keyed_accounts, vote)?; - let vote_state = VoteState::deserialize(&vote_account.userdata).unwrap(); - Ok(vote_state) - } #[test] fn test_voter_registration() { diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index dd6c482c37..3cc587a062 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -12,6 +12,7 @@ pub mod native_program; pub mod packet; pub mod payment_plan; pub mod pubkey; +pub mod rewards_program; pub mod shortvec; pub mod signature; pub mod storage_program; diff --git a/sdk/src/rewards_program.rs b/sdk/src/rewards_program.rs new file mode 100644 index 0000000000..eb1f3183d8 --- /dev/null +++ b/sdk/src/rewards_program.rs @@ -0,0 +1,24 @@ +use crate::pubkey::Pubkey; +use bincode::serialized_size; + +pub const REWARDS_PROGRAM_ID: [u8; 32] = [ + 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +]; + +pub fn check_id(program_id: &Pubkey) -> bool { + program_id.as_ref() == REWARDS_PROGRAM_ID +} + +pub fn id() -> Pubkey { + Pubkey::new(&REWARDS_PROGRAM_ID) +} + +#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] +struct RewardsState {} + +/// Upper limit on the size of the Rewards State. +pub fn get_max_size() -> usize { + let rewards_state = RewardsState::default(); + serialized_size(&rewards_state).unwrap() as usize +} diff --git a/sdk/src/vote_program.rs b/sdk/src/vote_program.rs index 3aa16e1c00..12a43c7547 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 -const MAX_VOTE_HISTORY: usize = 32; +pub const MAX_VOTE_HISTORY: usize = 32; #[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Vote {