add authorized parameters to vote api (#6072)

* add authorized parameters to vote api

* code review
This commit is contained in:
Rob Walker
2019-09-25 13:53:49 -07:00
committed by GitHub
parent 62429585ba
commit 43795193c4
9 changed files with 578 additions and 217 deletions

View File

@ -14,7 +14,7 @@ use solana_stake_api::stake_instruction;
use solana_stake_api::stake_instruction::process_instruction;
use solana_stake_api::stake_state::StakeState;
use solana_vote_api::vote_instruction;
use solana_vote_api::vote_state::{Vote, VoteState};
use solana_vote_api::vote_state::{Vote, VoteInit, VoteState};
use std::sync::Arc;
fn fill_epoch_with_votes(
@ -76,8 +76,12 @@ fn test_stake_account_delegate() {
let message = Message::new(vote_instruction::create_account(
&mint_pubkey,
&vote_pubkey,
&node_pubkey,
std::u8::MAX / 2,
&VoteInit {
node_pubkey,
authorized_voter: vote_pubkey,
authorized_withdrawer: vote_pubkey,
commission: std::u8::MAX / 2,
},
10,
));
bank_client

View File

@ -3,7 +3,7 @@
use crate::{
id,
vote_state::{self, Vote, VoteState},
vote_state::{self, Vote, VoteAuthorize, VoteInit, VoteState},
};
use bincode::deserialize;
use log::*;
@ -51,11 +51,11 @@ impl std::error::Error for VoteError {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum VoteInstruction {
/// Initialize the VoteState for this `vote account`
/// takes a node_pubkey and commission
InitializeAccount(Pubkey, u8),
InitializeAccount(VoteInit),
/// Authorize a voter to send signed votes.
AuthorizeVoter(Pubkey),
/// Authorize a voter to send signed votes or a withdrawer
/// to withdraw
Authorize(Pubkey, VoteAuthorize),
/// A Vote instruction with recent votes
Vote(Vote),
@ -64,44 +64,38 @@ pub enum VoteInstruction {
Withdraw(u64),
}
fn initialize_account(vote_pubkey: &Pubkey, node_pubkey: &Pubkey, commission: u8) -> Instruction {
fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
let account_metas = vec![AccountMeta::new(*vote_pubkey, false)];
Instruction::new(
id(),
&VoteInstruction::InitializeAccount(*node_pubkey, commission),
&VoteInstruction::InitializeAccount(*vote_init),
account_metas,
)
}
pub fn minimum_balance() -> u64 {
let rent_calculator = solana_sdk::rent_calculator::RentCalculator::default();
rent_calculator.minimum_balance(VoteState::size_of())
}
pub fn create_account(
from_pubkey: &Pubkey,
vote_pubkey: &Pubkey,
node_pubkey: &Pubkey,
commission: u8,
vote_init: &VoteInit,
lamports: u64,
) -> Vec<Instruction> {
let space = VoteState::size_of() as u64;
let create_ix =
system_instruction::create_account(from_pubkey, vote_pubkey, lamports, space, &id());
let init_ix = initialize_account(vote_pubkey, node_pubkey, commission);
let init_ix = initialize_account(vote_pubkey, vote_init);
vec![create_ix, init_ix]
}
// for instructions that whose authorized signer may differ from the account's pubkey
fn metas_for_authorized_signer(
vote_pubkey: &Pubkey,
authorized_voter_pubkey: &Pubkey, // currently authorized
account_pubkey: &Pubkey,
authorized_signer: &Pubkey, // currently authorized
other_params: &[AccountMeta],
) -> Vec<AccountMeta> {
let is_own_signer = authorized_voter_pubkey == vote_pubkey;
let is_own_signer = authorized_signer == account_pubkey;
// vote account
let mut account_metas = vec![AccountMeta::new(*vote_pubkey, is_own_signer)];
let mut account_metas = vec![AccountMeta::new(*account_pubkey, is_own_signer)];
for meta in other_params {
account_metas.push(meta.clone());
@ -109,22 +103,23 @@ fn metas_for_authorized_signer(
// append signer at the end
if !is_own_signer {
account_metas.push(AccountMeta::new_credit_only(*authorized_voter_pubkey, true)) // signer
account_metas.push(AccountMeta::new_credit_only(*authorized_signer, true)) // signer
}
account_metas
}
pub fn authorize_voter(
pub fn authorize(
vote_pubkey: &Pubkey,
authorized_voter_pubkey: &Pubkey, // currently authorized
new_authorized_voter_pubkey: &Pubkey,
authorized_pubkey: &Pubkey, // currently authorized
new_authorized_pubkey: &Pubkey,
vote_authorize: VoteAuthorize,
) -> Instruction {
let account_metas = metas_for_authorized_signer(vote_pubkey, authorized_voter_pubkey, &[]);
let account_metas = metas_for_authorized_signer(vote_pubkey, authorized_pubkey, &[]);
Instruction::new(
id(),
&VoteInstruction::AuthorizeVoter(*new_authorized_voter_pubkey),
&VoteInstruction::Authorize(*new_authorized_pubkey, vote_authorize),
account_metas,
)
}
@ -144,11 +139,17 @@ pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote)
Instruction::new(id(), &VoteInstruction::Vote(vote), account_metas)
}
pub fn withdraw(vote_pubkey: &Pubkey, lamports: u64, to_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*vote_pubkey, true),
AccountMeta::new_credit_only(*to_pubkey, false),
];
pub fn withdraw(
vote_pubkey: &Pubkey,
withdrawer_pubkey: &Pubkey,
lamports: u64,
to_pubkey: &Pubkey,
) -> Instruction {
let account_metas = metas_for_authorized_signer(
vote_pubkey,
withdrawer_pubkey,
&[AccountMeta::new_credit_only(*to_pubkey, false)],
);
Instruction::new(id(), &VoteInstruction::Withdraw(lamports), account_metas)
}
@ -173,11 +174,11 @@ pub fn process_instruction(
// TODO: data-driven unpack and dispatch of KeyedAccounts
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
VoteInstruction::InitializeAccount(node_pubkey, commission) => {
vote_state::initialize_account(me, &node_pubkey, commission)
VoteInstruction::InitializeAccount(vote_init) => {
vote_state::initialize_account(me, &vote_init)
}
VoteInstruction::AuthorizeVoter(voter_pubkey) => {
vote_state::authorize_voter(me, rest, &voter_pubkey)
VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
vote_state::authorize(me, rest, &voter_pubkey, vote_authorize)
}
VoteInstruction::Vote(vote) => {
datapoint_info!("vote-native", ("count", 1, i64));
@ -198,7 +199,10 @@ pub fn process_instruction(
if rest.is_empty() {
Err(InstructionError::InvalidInstructionData)?;
}
vote_state::withdraw(me, lamports, &mut rest[0])
let (to, rest) = rest.split_at_mut(1);
let to = &mut to[0];
vote_state::withdraw(me, rest, lamports, to)
}
}
}
@ -251,8 +255,7 @@ mod tests {
let instructions = create_account(
&Pubkey::default(),
&Pubkey::default(),
&Pubkey::default(),
0,
&VoteInit::default(),
100,
);
assert_eq!(
@ -268,10 +271,20 @@ mod tests {
Err(InstructionError::InvalidAccountData),
);
assert_eq!(
process_instruction(&authorize_voter(
process_instruction(&authorize(
&Pubkey::default(),
&Pubkey::default(),
&Pubkey::default(),
VoteAuthorize::Voter,
)),
Err(InstructionError::InvalidAccountData),
);
assert_eq!(
process_instruction(&withdraw(
&Pubkey::default(),
&Pubkey::default(),
0,
&Pubkey::default()
)),
Err(InstructionError::InvalidAccountData),
);
@ -285,4 +298,44 @@ mod tests {
assert!(minimum_balance as f64 / 2f64.powf(34.0) < 0.02)
}
#[test]
fn test_metas_for_authorized_signer() {
let account_pubkey = Pubkey::new_rand();
let authorized_signer = Pubkey::new_rand();
assert_eq!(
metas_for_authorized_signer(&account_pubkey, &authorized_signer, &[]).len(),
2
);
assert_eq!(
metas_for_authorized_signer(&account_pubkey, &account_pubkey, &[]).len(),
1
);
}
#[test]
fn test_custom_error_decode() {
use num_traits::FromPrimitive;
fn pretty_err<T>(err: InstructionError) -> String
where
T: 'static + std::error::Error + DecodeError<T> + FromPrimitive,
{
if let InstructionError::CustomError(code) = err {
let specific_error: T = T::decode_custom_error_to_enum(code).unwrap();
format!(
"{:?}: {}::{:?} - {}",
err,
T::type_of(),
specific_error,
specific_error,
)
} else {
"".to_string()
}
}
assert_eq!(
"CustomError(0): VoteError::VoteTooOld - vote already recorded or not in slot hashes history",
pretty_err::<VoteError>(VoteError::VoteTooOld.into())
)
}
}

View File

@ -67,14 +67,33 @@ impl Lockout {
}
}
#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
pub struct VoteInit {
pub node_pubkey: Pubkey,
pub authorized_voter: Pubkey,
pub authorized_withdrawer: Pubkey,
pub commission: u8,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
pub enum VoteAuthorize {
Voter,
Withdrawer,
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct VoteState {
pub votes: VecDeque<Lockout>,
/// the node that votes in this account
pub node_pubkey: Pubkey,
pub authorized_voter_pubkey: Pubkey,
/// the signer for vote transactions
pub authorized_voter: Pubkey,
/// the signer for withdrawals
pub authorized_withdrawer: Pubkey,
/// fraction of std::u8::MAX that represents what part of a rewards
/// payout should be given to this VoteAccount
pub commission: u8,
pub votes: VecDeque<Lockout>,
pub root_slot: Option<u64>,
/// clock epoch
@ -91,11 +110,12 @@ pub struct VoteState {
}
impl VoteState {
pub fn new(vote_pubkey: &Pubkey, node_pubkey: &Pubkey, commission: u8) -> Self {
pub fn new(vote_init: &VoteInit) -> Self {
Self {
node_pubkey: *node_pubkey,
authorized_voter_pubkey: *vote_pubkey,
commission,
node_pubkey: vote_init.node_pubkey,
authorized_voter: vote_init.authorized_voter,
authorized_withdrawer: vote_init.authorized_withdrawer,
commission: vote_init.commission,
..VoteState::default()
}
}
@ -313,39 +333,69 @@ impl VoteState {
}
}
/// Authorize the given pubkey to sign votes. This may be called multiple times,
/// Authorize the given pubkey to withdraw or sign votes. This may be called multiple times,
/// but will implicitly withdraw authorization from the previously authorized
/// voter. The default voter is the owner of the vote account's pubkey.
pub fn authorize_voter(
/// key
pub fn authorize(
vote_account: &mut KeyedAccount,
other_signers: &[KeyedAccount],
authorized_voter_pubkey: &Pubkey,
authorized: &Pubkey,
vote_authorize: VoteAuthorize,
) -> Result<(), InstructionError> {
let mut vote_state: VoteState = vote_account.state()?;
// clock authorized signer must say "yay"
let authorized = Some(&vote_state.authorized_voter_pubkey);
if vote_account.signer_key() != authorized
// current authorized signer must say "yay"
match vote_authorize {
VoteAuthorize::Voter => {
verify_authorized_signer(&vote_state.authorized_voter, vote_account, other_signers)?;
vote_state.authorized_voter = *authorized;
}
VoteAuthorize::Withdrawer => {
verify_authorized_signer(
&vote_state.authorized_withdrawer,
vote_account,
other_signers,
)?;
vote_state.authorized_withdrawer = *authorized;
}
}
vote_account.set_state(&vote_state)
}
fn verify_authorized_signer(
authorized: &Pubkey,
account: &KeyedAccount,
other_signers: &[KeyedAccount],
) -> Result<(), InstructionError> {
let authorized = Some(authorized);
// find a signer that matches authorized
if account.signer_key() != authorized
&& other_signers
.iter()
.all(|account| account.signer_key() != authorized)
{
return Err(InstructionError::MissingRequiredSignature);
}
vote_state.authorized_voter_pubkey = *authorized_voter_pubkey;
vote_account.set_state(&vote_state)
Ok(())
}
/// Withdraw funds from the vote account
pub fn withdraw(
vote_account: &mut KeyedAccount,
other_signers: &[KeyedAccount],
lamports: u64,
to_account: &mut KeyedAccount,
) -> Result<(), InstructionError> {
if vote_account.signer_key().is_none() {
return Err(InstructionError::MissingRequiredSignature);
}
let vote_state: VoteState = vote_account.state()?;
verify_authorized_signer(
&vote_state.authorized_withdrawer,
vote_account,
other_signers,
)?;
if vote_account.account.lamports < lamports {
return Err(InstructionError::InsufficientFunds);
}
@ -359,19 +409,14 @@ pub fn withdraw(
/// that the transaction must be signed by the staker's keys
pub fn initialize_account(
vote_account: &mut KeyedAccount,
node_pubkey: &Pubkey,
commission: u8,
vote_init: &VoteInit,
) -> Result<(), InstructionError> {
let vote_state: VoteState = vote_account.state()?;
if vote_state.authorized_voter_pubkey != Pubkey::default() {
if vote_state.authorized_voter != Pubkey::default() {
return Err(InstructionError::AccountAlreadyInitialized);
}
vote_account.set_state(&VoteState::new(
vote_account.unsigned_key(),
node_pubkey,
commission,
))
vote_account.set_state(&VoteState::new(vote_init))
}
pub fn process_vote(
@ -383,19 +428,11 @@ pub fn process_vote(
) -> Result<(), InstructionError> {
let mut vote_state: VoteState = vote_account.state()?;
if vote_state.authorized_voter_pubkey == Pubkey::default() {
if vote_state.authorized_voter == Pubkey::default() {
return Err(InstructionError::UninitializedAccount);
}
let authorized = Some(&vote_state.authorized_voter_pubkey);
// find a signer that matches the authorized_voter_pubkey
if vote_account.signer_key() != authorized
&& other_signers
.iter()
.all(|account| account.signer_key() != authorized)
{
return Err(InstructionError::MissingRequiredSignature);
}
verify_authorized_signer(&vote_state.authorized_voter, vote_account, other_signers)?;
vote_state.process_vote(vote, slot_hashes, clock.epoch)?;
vote_account.set_state(&vote_state)
@ -410,9 +447,14 @@ pub fn create_account(
) -> Account {
let mut vote_account = Account::new(lamports, VoteState::size_of(), &id());
VoteState::new(vote_pubkey, node_pubkey, commission)
.to(&mut vote_account)
.unwrap();
VoteState::new(&VoteInit {
node_pubkey: *node_pubkey,
authorized_voter: *vote_pubkey,
authorized_withdrawer: *vote_pubkey,
commission,
})
.to(&mut vote_account)
.unwrap();
vote_account
}
@ -427,6 +469,17 @@ mod tests {
const MAX_RECENT_VOTES: usize = 16;
impl VoteState {
pub fn new_for_test(auth_pubkey: &Pubkey) -> Self {
Self::new(&VoteInit {
node_pubkey: Pubkey::new_rand(),
authorized_voter: *auth_pubkey,
authorized_withdrawer: *auth_pubkey,
commission: 0,
})
}
}
#[test]
fn test_initialize_vote_account() {
let vote_account_pubkey = Pubkey::new_rand();
@ -436,11 +489,27 @@ mod tests {
//init should pass
let mut vote_account = KeyedAccount::new(&vote_account_pubkey, false, &mut vote_account);
let res = initialize_account(&mut vote_account, &node_pubkey, 0);
let res = initialize_account(
&mut vote_account,
&VoteInit {
node_pubkey,
authorized_voter: vote_account_pubkey,
authorized_withdrawer: vote_account_pubkey,
commission: 0,
},
);
assert_eq!(res, Ok(()));
// reinit should fail
let res = initialize_account(&mut vote_account, &node_pubkey, 0);
let res = initialize_account(
&mut vote_account,
&VoteInit {
node_pubkey,
authorized_voter: vote_account_pubkey,
authorized_withdrawer: vote_account_pubkey,
commission: 0,
},
);
assert_eq!(res, Err(InstructionError::AccountAlreadyInitialized));
}
@ -504,7 +573,7 @@ mod tests {
let (vote_pubkey, vote_account) = create_test_account();
let vote_state: VoteState = vote_account.state().unwrap();
assert_eq!(vote_state.authorized_voter_pubkey, vote_pubkey);
assert_eq!(vote_state.authorized_voter, vote_pubkey);
assert!(vote_state.votes.is_empty());
}
@ -582,21 +651,23 @@ mod tests {
// another voter
let authorized_voter_pubkey = Pubkey::new_rand();
let res = authorize_voter(
let res = authorize(
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
&[],
&authorized_voter_pubkey,
VoteAuthorize::Voter,
);
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
let res = authorize_voter(
let res = authorize(
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
&[],
&authorized_voter_pubkey,
VoteAuthorize::Voter,
);
assert_eq!(res, Ok(()));
// verify authorized_voter_pubkey can authorize authorized_voter_pubkey ;)
let res = authorize_voter(
let res = authorize(
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
&[KeyedAccount::new(
&authorized_voter_pubkey,
@ -604,6 +675,31 @@ mod tests {
&mut Account::default(),
)],
&authorized_voter_pubkey,
VoteAuthorize::Voter,
);
assert_eq!(res, Ok(()));
// authorize another withdrawer
// another voter
let authorized_withdrawer_pubkey = Pubkey::new_rand();
let res = authorize(
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
&[],
&authorized_withdrawer_pubkey,
VoteAuthorize::Withdrawer,
);
assert_eq!(res, Ok(()));
// verify authorized_withdrawer can authorize authorized_withdrawer ;)
let res = authorize(
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
&[KeyedAccount::new(
&authorized_withdrawer_pubkey,
true,
&mut Account::default(),
)],
&authorized_withdrawer_pubkey,
VoteAuthorize::Withdrawer,
);
assert_eq!(res, Ok(()));
@ -678,7 +774,7 @@ mod tests {
#[test]
fn test_vote_double_lockout_after_expiration() {
let voter_pubkey = Pubkey::new_rand();
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
for i in 0..3 {
vote_state.process_slot_vote_unchecked(i as u64);
@ -706,7 +802,7 @@ mod tests {
#[test]
fn test_expire_multiple_votes() {
let voter_pubkey = Pubkey::new_rand();
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
for i in 0..3 {
vote_state.process_slot_vote_unchecked(i as u64);
@ -737,7 +833,7 @@ mod tests {
#[test]
fn test_vote_credits() {
let voter_pubkey = Pubkey::new_rand();
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
for i in 0..MAX_LOCKOUT_HISTORY {
vote_state.process_slot_vote_unchecked(i as u64);
@ -756,7 +852,7 @@ mod tests {
#[test]
fn test_duplicate_vote() {
let voter_pubkey = Pubkey::new_rand();
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
vote_state.process_slot_vote_unchecked(0);
vote_state.process_slot_vote_unchecked(1);
vote_state.process_slot_vote_unchecked(0);
@ -768,7 +864,7 @@ mod tests {
#[test]
fn test_nth_recent_vote() {
let voter_pubkey = Pubkey::new_rand();
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
for i in 0..MAX_LOCKOUT_HISTORY {
vote_state.process_slot_vote_unchecked(i as u64);
}
@ -799,9 +895,9 @@ mod tests {
#[test]
fn test_process_missed_votes() {
let account_a = Pubkey::new_rand();
let mut vote_state_a = VoteState::new(&account_a, &Pubkey::new_rand(), 0);
let mut vote_state_a = VoteState::new_for_test(&account_a);
let account_b = Pubkey::new_rand();
let mut vote_state_b = VoteState::new(&account_b, &Pubkey::new_rand(), 0);
let mut vote_state_b = VoteState::new_for_test(&account_b);
// process some votes on account a
(0..5)
@ -821,7 +917,7 @@ mod tests {
#[test]
fn test_process_vote_skips_old_vote() {
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let mut vote_state = VoteState::default();
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
@ -836,7 +932,7 @@ mod tests {
#[test]
fn test_check_slots_are_valid_vote_empty_slot_hashes() {
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let vote_state = VoteState::default();
let vote = Vote::new(vec![0], Hash::default());
assert_eq!(
@ -847,7 +943,7 @@ mod tests {
#[test]
fn test_check_slots_are_valid_new_vote() {
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let vote_state = VoteState::default();
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
@ -859,7 +955,7 @@ mod tests {
#[test]
fn test_check_slots_are_valid_bad_hash() {
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let vote_state = VoteState::default();
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
@ -871,7 +967,7 @@ mod tests {
#[test]
fn test_check_slots_are_valid_bad_slot() {
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let vote_state = VoteState::default();
let vote = Vote::new(vec![1], Hash::default());
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
@ -883,7 +979,7 @@ mod tests {
#[test]
fn test_check_slots_are_valid_duplicate_vote() {
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let mut vote_state = VoteState::default();
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
@ -896,7 +992,7 @@ mod tests {
#[test]
fn test_check_slots_are_valid_next_vote() {
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let mut vote_state = VoteState::default();
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
@ -912,7 +1008,7 @@ mod tests {
#[test]
fn test_check_slots_are_valid_next_vote_only() {
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let mut vote_state = VoteState::default();
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
@ -925,17 +1021,28 @@ mod tests {
Ok(())
);
}
#[test]
fn test_process_vote_empty_slots() {
let mut vote_state = VoteState::default();
let vote = Vote::new(vec![], Hash::default());
assert_eq!(
vote_state.process_vote(&vote, &[], 0),
Err(VoteError::EmptySlots)
);
}
#[test]
fn test_vote_state_commission_split() {
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
let vote_state = VoteState::default();
assert_eq!(vote_state.commission_split(1.0), (0.0, 1.0, false));
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), std::u8::MAX);
let mut vote_state = VoteState::default();
vote_state.commission = std::u8::MAX;
assert_eq!(vote_state.commission_split(1.0), (1.0, 0.0, false));
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), std::u8::MAX / 2);
vote_state.commission = std::u8::MAX / 2;
let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10.0);
assert_eq!(
@ -948,9 +1055,10 @@ mod tests {
fn test_vote_state_withdraw() {
let (vote_pubkey, mut vote_account) = create_test_account();
// unsigned
// unsigned request
let res = withdraw(
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
&[],
0,
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut Account::default()),
);
@ -959,6 +1067,7 @@ mod tests {
// insufficient funds
let res = withdraw(
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
&[],
101,
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut Account::default()),
);
@ -969,6 +1078,25 @@ mod tests {
let lamports = vote_account.lamports;
let res = withdraw(
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
&[],
lamports,
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut to_account),
);
assert_eq!(res, Ok(()));
assert_eq!(vote_account.lamports, 0);
assert_eq!(to_account.lamports, lamports);
// reset balance, verify that authorized_withdrawer works
vote_account.lamports = lamports;
to_account.lamports = 0;
let mut authorized_withdrawer_account = Account::new(0, 0, &vote_pubkey);
let res = withdraw(
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
&[KeyedAccount::new(
&vote_pubkey,
true,
&mut authorized_withdrawer_account,
)],
lamports,
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut to_account),
);