From 55f06f5bade80fc38c6f6a52e9d43f4c36a07f02 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 15 Feb 2019 08:42:09 -0700 Subject: [PATCH] Make vote_program available to reward_program tests Making `solana_vote_program` is not an option because then vote_program's entrypoint conflicts with reward_program's entrypoint. This unfortunately turns the SDK into a dumping ground for all things shared between vote_program and other programs. Better would be to create a solana-vote-api crate similar to the solana-rewards-api crate. --- Cargo.lock | 3 - programs/native/rewards/Cargo.toml | 1 - programs/native/rewards/src/lib.rs | 17 ++-- programs/native/vote/Cargo.toml | 4 +- programs/native/vote/src/lib.rs | 121 +---------------------------- sdk/src/vote_program.rs | 116 ++++++++++++++++++++++++++- 6 files changed, 124 insertions(+), 138 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dcfc23ed8d..c27de12999 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2225,7 +2225,6 @@ dependencies = [ "solana-rewards-api 0.12.0", "solana-runtime 0.12.0", "solana-sdk 0.12.0", - "solana-vote-program 0.12.0", ] [[package]] @@ -2291,8 +2290,6 @@ version = "0.12.0" dependencies = [ "bincode 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", "solana-logger 0.12.0", "solana-metrics 0.12.0", "solana-runtime 0.12.0", diff --git a/programs/native/rewards/Cargo.toml b/programs/native/rewards/Cargo.toml index 77513f9267..4dce4bccaf 100644 --- a/programs/native/rewards/Cargo.toml +++ b/programs/native/rewards/Cargo.toml @@ -19,7 +19,6 @@ solana-sdk = { path = "../../../sdk", version = "0.12.0" } solana-rewards-api = { path = "../rewards_api", version = "0.12.0" } [dev-dependencies] -solana-vote-program = { path = "../vote", version = "0.12.0" } solana-runtime = { path = "../../../runtime", version = "0.12.0" } [lib] diff --git a/programs/native/rewards/src/lib.rs b/programs/native/rewards/src/lib.rs index e67455c13f..23f7c38bcb 100644 --- a/programs/native/rewards/src/lib.rs +++ b/programs/native/rewards/src/lib.rs @@ -95,7 +95,6 @@ mod tests { use solana_sdk::account::Account; 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(); @@ -126,8 +125,8 @@ mod tests { 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( + let mut vote_account = vote_program::create_vote_account(100); + vote_program::register_and_deserialize( &from_id, &mut from_account, &vote_id, @@ -137,19 +136,15 @@ mod tests { 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(); + let vote_state = + 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(); + 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(); diff --git a/programs/native/vote/Cargo.toml b/programs/native/vote/Cargo.toml index 3e460e2ada..a04991ceca 100644 --- a/programs/native/vote/Cargo.toml +++ b/programs/native/vote/Cargo.toml @@ -11,8 +11,6 @@ edition = "2018" [dependencies] bincode = "1.1.1" log = "0.4.2" -serde = "1.0.87" -serde_derive = "1.0.87" solana-logger = { path = "../../../logger", version = "0.12.0" } solana-metrics = { path = "../../../metrics", version = "0.12.0" } solana-sdk = { path = "../../../sdk", version = "0.12.0" } @@ -22,5 +20,5 @@ solana-runtime = { path = "../../../runtime", version = "0.12.0" } [lib] name = "solana_vote_program" -crate-type = ["cdylib", "lib"] +crate-type = ["cdylib"] diff --git a/programs/native/vote/src/lib.rs b/programs/native/vote/src/lib.rs index 27f707bcd8..feea144fde 100644 --- a/programs/native/vote/src/lib.rs +++ b/programs/native/vote/src/lib.rs @@ -3,46 +3,11 @@ use bincode::deserialize; use log::*; -use solana_sdk::account::{Account, KeyedAccount}; +use solana_sdk::account::KeyedAccount; use solana_sdk::native_program::ProgramError; use solana_sdk::pubkey::Pubkey; use solana_sdk::solana_entrypoint; -use solana_sdk::vote_program::{self, Vote, VoteInstruction, VoteState}; - -// TODO: Deprecate the RegisterAccount instruction and its awkward delegation -// semantics. -fn register(keyed_accounts: &mut [KeyedAccount]) -> Result<(), ProgramError> { - if !vote_program::check_id(&keyed_accounts[1].account.owner) { - error!("account[1] is not assigned to the VOTE_PROGRAM"); - Err(ProgramError::InvalidArgument)?; - } - - // TODO: This assumes keyed_accounts[0] is the SystemInstruction::CreateAccount - // that created keyed_accounts[1]. Putting any other signed instruction in - // keyed_accounts[0] would allow the owner to highjack the vote account and - // insert itself into the leader rotation. - let from_id = keyed_accounts[0].signer_key().unwrap(); - - // Awkwardly configure the voting account to claim that the account that - // initially funded it is both the identity of the staker and the fullnode - // that should sign blocks on behalf of the staker. - let vote_state = VoteState::new(*from_id, *from_id); - vote_state.serialize(&mut keyed_accounts[1].account.userdata)?; - - Ok(()) -} - -fn process_vote(keyed_accounts: &mut [KeyedAccount], vote: Vote) -> Result<(), ProgramError> { - if !vote_program::check_id(&keyed_accounts[0].account.owner) { - error!("account[0] is not assigned to the VOTE_PROGRAM"); - Err(ProgramError::InvalidArgument)?; - } - - let mut vote_state = VoteState::deserialize(&keyed_accounts[0].account.userdata)?; - vote_state.process_vote(vote); - vote_state.serialize(&mut keyed_accounts[0].account.userdata)?; - Ok(()) -} +use solana_sdk::vote_program::{self, VoteInstruction}; solana_entrypoint!(entrypoint); fn entrypoint( @@ -63,7 +28,7 @@ fn entrypoint( } match deserialize(data).map_err(|_| ProgramError::InvalidUserdata)? { - VoteInstruction::RegisterAccount => register(keyed_accounts), + VoteInstruction::RegisterAccount => vote_program::register(keyed_accounts), VoteInstruction::Vote(vote) => { debug!("{:?} by {}", vote, keyed_accounts[0].signer_key().unwrap()); solana_metrics::submit( @@ -71,85 +36,7 @@ fn entrypoint( .add_field("count", solana_metrics::influxdb::Value::Integer(1)) .to_owned(), ); - process_vote(keyed_accounts, vote) + vote_program::process_vote(keyed_accounts, vote) } } } - -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::signature::{Keypair, KeypairUtil}; - - #[test] - fn test_voter_registration() { - 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); - - let vote_state = - register_and_deserialize(&from_id, &mut from_account, &vote_id, &mut vote_account) - .unwrap(); - assert_eq!(vote_state.node_id, from_id); - assert!(vote_state.votes.is_empty()); - } - - #[test] - fn test_vote() { - 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(); - - 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] - fn test_vote_without_registration() { - let vote_id = Keypair::new().pubkey(); - let mut vote_account = create_vote_account(100); - - let vote = Vote::new(1); - let vote_state = vote_and_deserialize(&vote_id, &mut vote_account, vote.clone()).unwrap(); - assert_eq!(vote_state.node_id, Pubkey::default()); - assert_eq!(vote_state.votes, vec![vote]); - } -} diff --git a/sdk/src/vote_program.rs b/sdk/src/vote_program.rs index 12a43c7547..aeae900812 100644 --- a/sdk/src/vote_program.rs +++ b/sdk/src/vote_program.rs @@ -1,9 +1,11 @@ //! Vote program //! Receive and processes votes from validators +use crate::account::{Account, KeyedAccount}; use crate::native_program::ProgramError; use crate::pubkey::Pubkey; use bincode::{deserialize, serialize_into, serialized_size, ErrorKind}; +use log::*; use std::collections::VecDeque; pub const VOTE_PROGRAM_ID: [u8; 32] = [ @@ -57,9 +59,9 @@ pub struct VoteState { pub fn get_max_size() -> usize { // Upper limit on the size of the Vote State. Equal to // sizeof(VoteState) when votes.len() is MAX_VOTE_HISTORY - let mut vote_program = VoteState::default(); - vote_program.votes = VecDeque::from(vec![Vote::default(); MAX_VOTE_HISTORY]); - serialized_size(&vote_program).unwrap() as usize + let mut vote_state = VoteState::default(); + vote_state.votes = VecDeque::from(vec![Vote::default(); MAX_VOTE_HISTORY]); + serialized_size(&vote_state).unwrap() as usize } impl VoteState { @@ -111,9 +113,76 @@ impl VoteState { } } +// TODO: Deprecate the RegisterAccount instruction and its awkward delegation +// semantics. +pub fn register(keyed_accounts: &mut [KeyedAccount]) -> Result<(), ProgramError> { + if !check_id(&keyed_accounts[1].account.owner) { + error!("account[1] is not assigned to the VOTE_PROGRAM"); + Err(ProgramError::InvalidArgument)?; + } + + // TODO: This assumes keyed_accounts[0] is the SystemInstruction::CreateAccount + // that created keyed_accounts[1]. Putting any other signed instruction in + // keyed_accounts[0] would allow the owner to highjack the vote account and + // insert itself into the leader rotation. + let from_id = keyed_accounts[0].signer_key().unwrap(); + + // Awkwardly configure the voting account to claim that the account that + // initially funded it is both the identity of the staker and the fullnode + // that should sign blocks on behalf of the staker. + let vote_state = VoteState::new(*from_id, *from_id); + vote_state.serialize(&mut keyed_accounts[1].account.userdata)?; + + Ok(()) +} + +pub fn process_vote(keyed_accounts: &mut [KeyedAccount], vote: Vote) -> Result<(), ProgramError> { + if !check_id(&keyed_accounts[0].account.owner) { + error!("account[0] is not assigned to the VOTE_PROGRAM"); + Err(ProgramError::InvalidArgument)?; + } + + let mut vote_state = VoteState::deserialize(&keyed_accounts[0].account.userdata)?; + vote_state.process_vote(vote); + vote_state.serialize(&mut keyed_accounts[0].account.userdata)?; + Ok(()) +} + +pub fn create_vote_account(tokens: u64) -> Account { + let space = get_max_size(); + Account::new(tokens, space, 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 crate::signature::{Keypair, KeypairUtil}; #[test] fn test_vote_serialize() { @@ -136,4 +205,45 @@ mod tests { vote_state.clear_credits(); assert_eq!(vote_state.credits(), 0); } + + #[test] + fn test_voter_registration() { + 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); + + let vote_state = + register_and_deserialize(&from_id, &mut from_account, &vote_id, &mut vote_account) + .unwrap(); + assert_eq!(vote_state.node_id, from_id); + assert!(vote_state.votes.is_empty()); + } + + #[test] + fn test_vote() { + 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(); + + 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] + fn test_vote_without_registration() { + let vote_id = Keypair::new().pubkey(); + let mut vote_account = create_vote_account(100); + + let vote = Vote::new(1); + let vote_state = vote_and_deserialize(&vote_id, &mut vote_account, vote.clone()).unwrap(); + assert_eq!(vote_state.node_id, Pubkey::default()); + assert_eq!(vote_state.votes, vec![vote]); + } }