get vote_instruction off bank for tests (#4086)
* get vote_instruction off bank for tests * clippy
This commit is contained in:
parent
408bdbce7a
commit
675a78aaa1
|
@ -2755,7 +2755,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",
|
||||
]
|
||||
|
||||
|
|
|
@ -17,9 +17,6 @@ solana-logger = { path = "../../logger", version = "0.15.0" }
|
|||
solana-metrics = { path = "../../metrics", version = "0.15.0" }
|
||||
solana-sdk = { path = "../../sdk", version = "0.15.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-runtime = { path = "../../runtime", version = "0.15.0" }
|
||||
|
||||
[lib]
|
||||
name = "solana_vote_api"
|
||||
crate-type = ["lib"]
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
pub mod vote_instruction;
|
||||
pub mod vote_state;
|
||||
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
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,
|
||||
16, 67, 252, 13, 163, 83, 128, 0, 0, 0, 0,
|
||||
];
|
||||
|
||||
pub fn check_id(program_id: &Pubkey) -> bool {
|
||||
program_id.as_ref() == VOTE_PROGRAM_ID
|
||||
}
|
||||
|
||||
pub fn id() -> Pubkey {
|
||||
Pubkey::new(&VOTE_PROGRAM_ID)
|
||||
}
|
||||
solana_sdk::solana_program_id!(VOTE_PROGRAM_ID);
|
||||
|
|
|
@ -71,10 +71,14 @@ pub fn process_instruction(
|
|||
trace!("process_instruction: {:?}", data);
|
||||
trace!("keyed_accounts: {:?}", keyed_accounts);
|
||||
|
||||
if keyed_accounts.is_empty() {
|
||||
Err(InstructionError::InvalidInstructionData)?;
|
||||
}
|
||||
|
||||
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
||||
VoteInstruction::InitializeAccount(node_id, commission) => {
|
||||
let mut vote_account = &mut keyed_accounts[0];
|
||||
vote_state::initialize_account(&mut vote_account, &node_id, commission)
|
||||
let vote_account = &mut keyed_accounts[0];
|
||||
vote_state::initialize_account(vote_account, &node_id, commission)
|
||||
}
|
||||
VoteInstruction::AuthorizeVoter(voter_id) => {
|
||||
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)
|
||||
}
|
||||
VoteInstruction::Vote(vote) => {
|
||||
VoteInstruction::Vote(votes) => {
|
||||
solana_metrics::submit(
|
||||
solana_metrics::influxdb::Point::new("vote-native")
|
||||
.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 = &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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::id;
|
||||
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(())
|
||||
}
|
||||
use solana_sdk::account::Account;
|
||||
|
||||
// these are for 100% coverage
|
||||
#[test]
|
||||
fn test_vote_bank_basic() {
|
||||
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);
|
||||
|
||||
fn test_vote_process_instruction_decode_bail() {
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(1, InstructionError::MissingRequiredSignature)
|
||||
super::process_instruction(&Pubkey::default(), &mut [], &[], 0,),
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
/// indicate with None for the 0% side
|
||||
/// indicate with false for was_split
|
||||
pub fn commission_split(&self, on: f64) -> (f64, f64, bool) {
|
||||
match self.commission {
|
||||
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,
|
||||
other_signers: &[KeyedAccount],
|
||||
votes: &[Vote],
|
||||
|
@ -277,7 +277,7 @@ pub fn vote(
|
|||
vote_account: &mut Account,
|
||||
vote: &Vote,
|
||||
) -> Result<VoteState, InstructionError> {
|
||||
process_vote(
|
||||
process_votes(
|
||||
&mut KeyedAccount::new(vote_id, true, vote_account),
|
||||
&[],
|
||||
&[vote.clone()],
|
||||
|
@ -355,7 +355,7 @@ mod tests {
|
|||
let vote = vec![Vote::new(1)];
|
||||
|
||||
// unsigned
|
||||
let res = process_vote(
|
||||
let res = process_votes(
|
||||
&mut KeyedAccount::new(&vote_id, false, &mut vote_account),
|
||||
&[],
|
||||
&vote,
|
||||
|
@ -363,7 +363,7 @@ mod tests {
|
|||
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
|
||||
|
||||
// unsigned
|
||||
let res = process_vote(
|
||||
let res = process_votes(
|
||||
&mut KeyedAccount::new(&vote_id, true, &mut vote_account),
|
||||
&[],
|
||||
&vote,
|
||||
|
@ -399,7 +399,7 @@ mod tests {
|
|||
|
||||
// not signed by authorized voter
|
||||
let vote = vec![Vote::new(2)];
|
||||
let res = process_vote(
|
||||
let res = process_votes(
|
||||
&mut KeyedAccount::new(&vote_id, true, &mut vote_account),
|
||||
&[],
|
||||
&vote,
|
||||
|
@ -408,7 +408,7 @@ mod tests {
|
|||
|
||||
// signed by authorized voter
|
||||
let vote = vec![Vote::new(2)];
|
||||
let res = process_vote(
|
||||
let res = process_votes(
|
||||
&mut KeyedAccount::new(&vote_id, false, &mut vote_account),
|
||||
&[KeyedAccount::new(
|
||||
&authorized_voter_id,
|
||||
|
@ -607,4 +607,23 @@ mod tests {
|
|||
vote_state_b.process_votes(&votes);
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
pub trait State<T> {
|
||||
fn state(&self) -> Result<T, InstructionError>;
|
||||
|
|
Loading…
Reference in New Issue