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)",
|
"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",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
Loading…
Reference in New Issue