get vote_instruction off bank for tests (#4086)

* get vote_instruction off bank for tests

* clippy
This commit is contained in:
Rob Walker 2019-04-30 15:11:08 -07:00 committed by GitHub
parent 408bdbce7a
commit 675a78aaa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 137 deletions

1
Cargo.lock generated
View File

@ -2755,7 +2755,6 @@ dependencies = [
"serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-logger 0.15.0", "solana-logger 0.15.0",
"solana-metrics 0.15.0", "solana-metrics 0.15.0",
"solana-runtime 0.15.0",
"solana-sdk 0.15.0", "solana-sdk 0.15.0",
] ]

View File

@ -17,9 +17,6 @@ solana-logger = { path = "../../logger", version = "0.15.0" }
solana-metrics = { path = "../../metrics", version = "0.15.0" } solana-metrics = { path = "../../metrics", version = "0.15.0" }
solana-sdk = { path = "../../sdk", version = "0.15.0" } solana-sdk = { path = "../../sdk", version = "0.15.0" }
[dev-dependencies]
solana-runtime = { path = "../../runtime", version = "0.15.0" }
[lib] [lib]
name = "solana_vote_api" name = "solana_vote_api"
crate-type = ["lib"] crate-type = ["lib"]

View File

@ -1,17 +1,9 @@
pub mod vote_instruction; pub mod vote_instruction;
pub mod vote_state; pub mod vote_state;
use solana_sdk::pubkey::Pubkey;
const VOTE_PROGRAM_ID: [u8; 32] = [ const VOTE_PROGRAM_ID: [u8; 32] = [
7, 97, 72, 29, 53, 116, 116, 187, 124, 77, 118, 36, 235, 211, 189, 179, 216, 53, 94, 115, 209, 7, 97, 72, 29, 53, 116, 116, 187, 124, 77, 118, 36, 235, 211, 189, 179, 216, 53, 94, 115, 209,
16, 67, 252, 13, 163, 83, 128, 0, 0, 0, 0, 16, 67, 252, 13, 163, 83, 128, 0, 0, 0, 0,
]; ];
pub fn check_id(program_id: &Pubkey) -> bool { solana_sdk::solana_program_id!(VOTE_PROGRAM_ID);
program_id.as_ref() == VOTE_PROGRAM_ID
}
pub fn id() -> Pubkey {
Pubkey::new(&VOTE_PROGRAM_ID)
}

View File

@ -71,10 +71,14 @@ pub fn process_instruction(
trace!("process_instruction: {:?}", data); trace!("process_instruction: {:?}", data);
trace!("keyed_accounts: {:?}", keyed_accounts); trace!("keyed_accounts: {:?}", keyed_accounts);
if keyed_accounts.is_empty() {
Err(InstructionError::InvalidInstructionData)?;
}
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
VoteInstruction::InitializeAccount(node_id, commission) => { VoteInstruction::InitializeAccount(node_id, commission) => {
let mut vote_account = &mut keyed_accounts[0]; let vote_account = &mut keyed_accounts[0];
vote_state::initialize_account(&mut vote_account, &node_id, commission) vote_state::initialize_account(vote_account, &node_id, commission)
} }
VoteInstruction::AuthorizeVoter(voter_id) => { VoteInstruction::AuthorizeVoter(voter_id) => {
let (vote_account, other_signers) = keyed_accounts.split_at_mut(1); let (vote_account, other_signers) = keyed_accounts.split_at_mut(1);
@ -82,7 +86,7 @@ pub fn process_instruction(
vote_state::authorize_voter(vote_account, other_signers, &voter_id) vote_state::authorize_voter(vote_account, other_signers, &voter_id)
} }
VoteInstruction::Vote(vote) => { VoteInstruction::Vote(votes) => {
solana_metrics::submit( solana_metrics::submit(
solana_metrics::influxdb::Point::new("vote-native") solana_metrics::influxdb::Point::new("vote-native")
.add_field("count", solana_metrics::influxdb::Value::Integer(1)) .add_field("count", solana_metrics::influxdb::Value::Integer(1))
@ -91,7 +95,7 @@ pub fn process_instruction(
let (vote_account, other_signers) = keyed_accounts.split_at_mut(1); let (vote_account, other_signers) = keyed_accounts.split_at_mut(1);
let vote_account = &mut vote_account[0]; let vote_account = &mut vote_account[0];
vote_state::process_vote(vote_account, other_signers, &vote) vote_state::process_votes(vote_account, other_signers, &votes)
} }
} }
} }
@ -99,121 +103,59 @@ pub fn process_instruction(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::id; use solana_sdk::account::Account;
use crate::vote_instruction;
use crate::vote_state::{Vote, VoteState};
use solana_runtime::bank::Bank;
use solana_runtime::bank_client::BankClient;
use solana_sdk::client::SyncClient;
use solana_sdk::genesis_block::GenesisBlock;
use solana_sdk::instruction::InstructionError;
use solana_sdk::message::Message;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::system_instruction;
use solana_sdk::transaction::{Result, TransactionError};
fn create_bank(lamports: u64) -> (Bank, Keypair) {
let (genesis_block, mint_keypair) = GenesisBlock::new(lamports);
let mut bank = Bank::new(&genesis_block);
bank.add_instruction_processor(id(), process_instruction);
(bank, mint_keypair)
}
fn create_vote_account(
bank_client: &BankClient,
from_keypair: &Keypair,
vote_id: &Pubkey,
lamports: u64,
) -> Result<()> {
let ixs = vote_instruction::create_account(
&from_keypair.pubkey(),
vote_id,
&Pubkey::new_rand(),
0,
lamports,
);
let message = Message::new(ixs);
bank_client
.send_message(&[from_keypair], message)
.map_err(|err| err.unwrap())?;
Ok(())
}
fn submit_vote(
bank_client: &BankClient,
vote_keypair: &Keypair,
tick_height: u64,
) -> Result<()> {
let vote_ix = vote_instruction::vote(&vote_keypair.pubkey(), vec![Vote::new(tick_height)]);
bank_client
.send_instruction(vote_keypair, vote_ix)
.map_err(|err| err.unwrap())?;
Ok(())
}
// these are for 100% coverage
#[test] #[test]
fn test_vote_bank_basic() { fn test_vote_process_instruction_decode_bail() {
let (bank, from_keypair) = create_bank(10_000);
let bank_client = BankClient::new(bank);
let vote_keypair = Keypair::new();
let vote_id = vote_keypair.pubkey();
create_vote_account(&bank_client, &from_keypair, &vote_id, 100).unwrap();
submit_vote(&bank_client, &vote_keypair, 0).unwrap();
let vote_account_data = bank_client.get_account_data(&vote_id).unwrap().unwrap();
let vote_state = VoteState::deserialize(&vote_account_data).unwrap();
assert_eq!(vote_state.votes.len(), 1);
}
#[test]
fn test_vote_via_bank_authorize_voter() {
let (bank, mallory_keypair) = create_bank(10_000);
let bank_client = BankClient::new(bank);
let vote_keypair = Keypair::new();
let vote_id = vote_keypair.pubkey();
create_vote_account(&bank_client, &mallory_keypair, &vote_id, 100).unwrap();
let mallory_id = mallory_keypair.pubkey();
let vote_ix = vote_instruction::authorize_voter(&vote_id, &mallory_id);
let message = Message::new(vec![vote_ix]);
assert!(bank_client.send_message(&[&vote_keypair], message).is_ok());
}
#[test]
fn test_vote_via_bank_with_no_signature() {
let (bank, mallory_keypair) = create_bank(10_000);
let bank_client = BankClient::new(bank);
let vote_keypair = Keypair::new();
let vote_id = vote_keypair.pubkey();
create_vote_account(&bank_client, &mallory_keypair, &vote_id, 100).unwrap();
let mallory_id = mallory_keypair.pubkey();
let mut vote_ix = vote_instruction::vote(&vote_id, vec![Vote::new(0)]);
vote_ix.accounts[0].is_signer = false; // <--- attack!! No signer required.
// Sneak in an instruction so that the transaction is signed but
// the 0th account in the second instruction is not! The program
// needs to check that it's signed.
let transfer_ix = system_instruction::transfer(&mallory_id, &vote_id, 1);
let message = Message::new(vec![transfer_ix, vote_ix]);
let result = bank_client.send_message(&[&mallory_keypair], message);
// And ensure there's no vote.
let vote_account_data = bank_client.get_account_data(&vote_id).unwrap().unwrap();
let vote_state = VoteState::deserialize(&vote_account_data).unwrap();
assert_eq!(vote_state.votes.len(), 0);
assert_eq!( assert_eq!(
result.unwrap_err().unwrap(), super::process_instruction(&Pubkey::default(), &mut [], &[], 0,),
TransactionError::InstructionError(1, InstructionError::MissingRequiredSignature) Err(InstructionError::InvalidInstructionData),
); );
} }
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_vote_process_instruction() {
let instructions = create_account(
&Pubkey::default(),
&Pubkey::default(),
&Pubkey::default(),
0,
100,
);
assert_eq!(
process_instruction(&instructions[1]),
Err(InstructionError::InvalidAccountData),
);
assert_eq!(
process_instruction(&vote(&Pubkey::default(), vec![Vote::default()])),
Err(InstructionError::InvalidAccountData),
);
assert_eq!(
process_instruction(&authorize_voter(&Pubkey::default(), &Pubkey::default())),
Err(InstructionError::InvalidAccountData),
);
}
} }

View File

@ -102,10 +102,10 @@ impl VoteState {
}) })
} }
/// returns commission split as (voter_portion, staker_portion) tuple /// returns commission split as (voter_portion, staker_portion, was_split) tuple
/// ///
/// if commission calculation is 100% one way or other, /// if commission calculation is 100% one way or other,
/// indicate with None for the 0% side /// indicate with false for was_split
pub fn commission_split(&self, on: f64) -> (f64, f64, bool) { pub fn commission_split(&self, on: f64) -> (f64, f64, bool) {
match self.commission { match self.commission {
0 => (0.0, on, false), 0 => (0.0, on, false),
@ -228,7 +228,7 @@ pub fn initialize_account(
)) ))
} }
pub fn process_vote( pub fn process_votes(
vote_account: &mut KeyedAccount, vote_account: &mut KeyedAccount,
other_signers: &[KeyedAccount], other_signers: &[KeyedAccount],
votes: &[Vote], votes: &[Vote],
@ -277,7 +277,7 @@ pub fn vote(
vote_account: &mut Account, vote_account: &mut Account,
vote: &Vote, vote: &Vote,
) -> Result<VoteState, InstructionError> { ) -> Result<VoteState, InstructionError> {
process_vote( process_votes(
&mut KeyedAccount::new(vote_id, true, vote_account), &mut KeyedAccount::new(vote_id, true, vote_account),
&[], &[],
&[vote.clone()], &[vote.clone()],
@ -355,7 +355,7 @@ mod tests {
let vote = vec![Vote::new(1)]; let vote = vec![Vote::new(1)];
// unsigned // unsigned
let res = process_vote( let res = process_votes(
&mut KeyedAccount::new(&vote_id, false, &mut vote_account), &mut KeyedAccount::new(&vote_id, false, &mut vote_account),
&[], &[],
&vote, &vote,
@ -363,7 +363,7 @@ mod tests {
assert_eq!(res, Err(InstructionError::MissingRequiredSignature)); assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
// unsigned // unsigned
let res = process_vote( let res = process_votes(
&mut KeyedAccount::new(&vote_id, true, &mut vote_account), &mut KeyedAccount::new(&vote_id, true, &mut vote_account),
&[], &[],
&vote, &vote,
@ -399,7 +399,7 @@ mod tests {
// not signed by authorized voter // not signed by authorized voter
let vote = vec![Vote::new(2)]; let vote = vec![Vote::new(2)];
let res = process_vote( let res = process_votes(
&mut KeyedAccount::new(&vote_id, true, &mut vote_account), &mut KeyedAccount::new(&vote_id, true, &mut vote_account),
&[], &[],
&vote, &vote,
@ -408,7 +408,7 @@ mod tests {
// signed by authorized voter // signed by authorized voter
let vote = vec![Vote::new(2)]; let vote = vec![Vote::new(2)];
let res = process_vote( let res = process_votes(
&mut KeyedAccount::new(&vote_id, false, &mut vote_account), &mut KeyedAccount::new(&vote_id, false, &mut vote_account),
&[KeyedAccount::new( &[KeyedAccount::new(
&authorized_voter_id, &authorized_voter_id,
@ -607,4 +607,23 @@ mod tests {
vote_state_b.process_votes(&votes); vote_state_b.process_votes(&votes);
assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b)); assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
} }
#[test]
fn test_vote_state_commission_split() {
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
assert_eq!(vote_state.commission_split(1.0), (0.0, 1.0, false));
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), std::u32::MAX);
assert_eq!(vote_state.commission_split(1.0), (1.0, 0.0, false));
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), std::u32::MAX / 2);
let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10.0);
assert_eq!(
(voter_portion.round(), staker_portion.round(), was_split),
(5.0, 5.0, true)
);
}
} }

View File

@ -32,6 +32,26 @@ macro_rules! solana_entrypoint(
) )
); );
#[macro_export]
macro_rules! solana_program_id(
($program_id:ident) => (
pub fn check_id(program_id: &solana_sdk::pubkey::Pubkey) -> bool {
program_id.as_ref() == $program_id
}
pub fn id() -> solana_sdk::pubkey::Pubkey {
solana_sdk::pubkey::Pubkey::new(&$program_id)
}
#[cfg(test)]
#[test]
fn test_program_id() {
assert!(check_id(&id()));
}
)
);
/// Conveinence trait to covert bincode errors to instruction errors. /// Conveinence trait to covert bincode errors to instruction errors.
pub trait State<T> { pub trait State<T> {
fn state(&self) -> Result<T, InstructionError>; fn state(&self) -> Result<T, InstructionError>;