diff --git a/Cargo.lock b/Cargo.lock index cf1583d636..0729b77ef2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2649,7 +2649,6 @@ dependencies = [ "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "solana-logger 0.15.0", "solana-metrics 0.15.0", - "solana-runtime 0.15.0", "solana-sdk 0.15.0", "solana-vote-api 0.15.0", ] diff --git a/programs/stake_api/Cargo.toml b/programs/stake_api/Cargo.toml index 2352604bad..e9cc79712c 100644 --- a/programs/stake_api/Cargo.toml +++ b/programs/stake_api/Cargo.toml @@ -18,9 +18,6 @@ solana-metrics = { path = "../../metrics", version = "0.15.0" } solana-sdk = { path = "../../sdk", version = "0.15.0" } solana-vote-api = { path = "../vote_api", version = "0.15.0" } -[dev-dependencies] -solana-runtime = { path = "../../runtime", version = "0.15.0" } - [lib] name = "solana_stake_api" crate-type = ["lib"] diff --git a/programs/stake_api/src/stake_instruction.rs b/programs/stake_api/src/stake_instruction.rs index b00260effd..1c531701cc 100644 --- a/programs/stake_api/src/stake_instruction.rs +++ b/programs/stake_api/src/stake_instruction.rs @@ -22,14 +22,14 @@ pub enum StakeInstruction { RedeemVoteCredits, } -pub fn create_account(from_id: &Pubkey, staker_id: &Pubkey, lamports: u64) -> Vec { - vec![system_instruction::create_account( +pub fn create_account(from_id: &Pubkey, staker_id: &Pubkey, lamports: u64) -> Instruction { + system_instruction::create_account( from_id, staker_id, lamports, std::mem::size_of::() as u64, &id(), - )] + ) } pub fn redeem_vote_credits( @@ -104,13 +104,59 @@ mod tests { use bincode::serialize; use solana_sdk::account::Account; + fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> { + let mut accounts = vec![]; + for _ in 0..instruction.accounts.len() { + accounts.push(Account::default()); + } + { + let mut keyed_accounts: Vec<_> = instruction + .accounts + .iter() + .zip(accounts.iter_mut()) + .map(|(meta, account)| KeyedAccount::new(&meta.pubkey, meta.is_signer, account)) + .collect(); + super::process_instruction( + &Pubkey::default(), + &mut keyed_accounts, + &instruction.data, + 0, + ) + } + } + + #[test] + fn test_stake_process_instruction() { + assert_eq!( + process_instruction(&create_account(&Pubkey::default(), &Pubkey::default(), 0)), + Err(InstructionError::InvalidInstructionData) // won't even decode ;) + ); + assert_eq!( + process_instruction(&redeem_vote_credits( + &Pubkey::default(), + &Pubkey::default(), + &Pubkey::default(), + &Pubkey::default() + )), + Err(InstructionError::InvalidAccountData), + ); + assert_eq!( + process_instruction(&delegate_stake( + &Pubkey::default(), + &Pubkey::default(), + &Pubkey::default() + )), + Err(InstructionError::InvalidAccountData), + ); + } + #[test] fn test_stake_process_instruction_decode_bail() { // these will not call stake_state, have bogus contents // gets the first check assert_eq!( - process_instruction( + super::process_instruction( &Pubkey::default(), &mut [KeyedAccount::new( &Pubkey::default(), @@ -123,12 +169,12 @@ mod tests { Err(InstructionError::InvalidInstructionData), ); - // gets the check in delegate_stake + // gets the sub-check for number of args assert_eq!( - process_instruction( + super::process_instruction( &Pubkey::default(), &mut [ - KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), true, &mut Account::default()), KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), ], &serialize(&StakeInstruction::DelegateStake).unwrap(), @@ -137,9 +183,8 @@ mod tests { Err(InstructionError::InvalidInstructionData), ); - // gets the check in redeem_vote_credits assert_eq!( - process_instruction( + super::process_instruction( &Pubkey::default(), &mut [ KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), @@ -151,6 +196,37 @@ mod tests { ), Err(InstructionError::InvalidInstructionData), ); + + // gets the check in delegate_stake + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &mut [ + KeyedAccount::new(&Pubkey::default(), true, &mut Account::default()), // from + KeyedAccount::new(&Pubkey::default(), true, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + ], + &serialize(&StakeInstruction::DelegateStake).unwrap(), + 0, + ), + Err(InstructionError::InvalidAccountData), + ); + + // gets the check in redeem_vote_credits + assert_eq!( + super::process_instruction( + &Pubkey::default(), + &mut [ + KeyedAccount::new(&Pubkey::default(), true, &mut Account::default()), // from + KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()), + ], + &serialize(&StakeInstruction::RedeemVoteCredits).unwrap(), + 0, + ), + Err(InstructionError::InvalidAccountData), + ); } } diff --git a/programs/stake_api/src/stake_state.rs b/programs/stake_api/src/stake_state.rs index e8fc61ccc8..206ce8f50f 100644 --- a/programs/stake_api/src/stake_state.rs +++ b/programs/stake_api/src/stake_state.rs @@ -3,10 +3,9 @@ //! * keep track of rewards //! * own mining pools -//use crate::{check_id, id}; -//use log::*; +use crate::id; use serde_derive::{Deserialize, Serialize}; -use solana_sdk::account::KeyedAccount; +use solana_sdk::account::{Account, KeyedAccount}; use solana_sdk::instruction::InstructionError; use solana_sdk::instruction_processor_utils::State; use solana_sdk::pubkey::Pubkey; @@ -40,7 +39,7 @@ const CREDITS_PER_YEAR: f64 = (365f64 * 24f64 * 3600f64) * TICKS_PER_SECOND / TI const STAKE_REWARD_TARGET_RATE: f64 = 0.20; #[cfg(test)] -const STAKE_GETS_PAID_EVERY_VOTE: u64 = 200_000_000; // if numbers above move, fix this +const STAKE_GETS_PAID_EVERY_VOTE: u64 = 200_000_000; // if numbers above (TICKS_YEAR) move, fix this impl StakeState { pub fn calculate_rewards( @@ -148,6 +147,24 @@ impl<'a> StakeAccount for KeyedAccount<'a> { } } +// utility function, used by Bank, tests, genesis +pub fn create_delegate_stake_account( + voter_id: &Pubkey, + vote_state: &VoteState, + lamports: u64, +) -> Account { + let mut stake_account = Account::new(lamports, std::mem::size_of::(), &id()); + + stake_account + .set_state(&StakeState::Delegate { + voter_id: *voter_id, + credits_observed: vote_state.credits(), + }) + .expect("set_state"); + + stake_account +} + #[cfg(test)] mod tests { use super::*; @@ -159,6 +176,8 @@ mod tests { #[test] fn test_stake_delegate_stake() { + dbg!(std::env::var("CARGO_FOO").unwrap_or("not set".to_string())); + let vote_keypair = Keypair::new(); let mut vote_state = VoteState::default(); for i in 0..1000 { @@ -176,6 +195,11 @@ mod tests { let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account); + { + let stake_state: StakeState = stake_keyed_account.state().unwrap(); + assert_eq!(stake_state, StakeState::default()); + } + assert_eq!( stake_keyed_account.delegate_stake(&vote_keyed_account), Err(InstructionError::MissingRequiredSignature) @@ -186,6 +210,13 @@ mod tests { .delegate_stake(&vote_keyed_account) .is_ok()); + // verify that create_delegate_stake_account() matches the + // resulting account from delegate_stake() + assert_eq!( + create_delegate_stake_account(&vote_pubkey, &vote_state, 0), + *stake_keyed_account.account, + ); + let stake_state: StakeState = stake_keyed_account.state().unwrap(); assert_eq!( stake_state, @@ -194,6 +225,7 @@ mod tests { credits_observed: vote_state.credits() } ); + let stake_state = StakeState::MiningPool; stake_keyed_account.set_state(&stake_state).unwrap(); assert!(stake_keyed_account