parent
6b99ab3a57
commit
d821fd29d6
|
@ -233,7 +233,7 @@ mod tests {
|
||||||
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_stake_program::stake_state;
|
use solana_stake_program::stake_state;
|
||||||
use solana_vote_program::vote_state;
|
use solana_vote_program::vote_state::{self, VoteStateVersions};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_block_commitment() {
|
fn test_block_commitment() {
|
||||||
|
@ -446,13 +446,15 @@ mod tests {
|
||||||
let mut vote_state1 = VoteState::from(&vote_account1).unwrap();
|
let mut vote_state1 = VoteState::from(&vote_account1).unwrap();
|
||||||
vote_state1.process_slot_vote_unchecked(3);
|
vote_state1.process_slot_vote_unchecked(3);
|
||||||
vote_state1.process_slot_vote_unchecked(5);
|
vote_state1.process_slot_vote_unchecked(5);
|
||||||
vote_state1.to(&mut vote_account1).unwrap();
|
let versioned = VoteStateVersions::Current(Box::new(vote_state1));
|
||||||
|
VoteState::to(&versioned, &mut vote_account1).unwrap();
|
||||||
bank.store_account(&pk1, &vote_account1);
|
bank.store_account(&pk1, &vote_account1);
|
||||||
|
|
||||||
let mut vote_state2 = VoteState::from(&vote_account2).unwrap();
|
let mut vote_state2 = VoteState::from(&vote_account2).unwrap();
|
||||||
vote_state2.process_slot_vote_unchecked(9);
|
vote_state2.process_slot_vote_unchecked(9);
|
||||||
vote_state2.process_slot_vote_unchecked(10);
|
vote_state2.process_slot_vote_unchecked(10);
|
||||||
vote_state2.to(&mut vote_account2).unwrap();
|
let versioned = VoteStateVersions::Current(Box::new(vote_state2));
|
||||||
|
VoteState::to(&versioned, &mut vote_account2).unwrap();
|
||||||
bank.store_account(&pk2, &vote_account2);
|
bank.store_account(&pk2, &vote_account2);
|
||||||
|
|
||||||
let commitment = AggregateCommitmentService::aggregate_commitment(&ancestors, &bank);
|
let commitment = AggregateCommitmentService::aggregate_commitment(&ancestors, &bank);
|
||||||
|
|
|
@ -483,7 +483,10 @@ pub mod test {
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_vote_program::{vote_instruction, vote_state::Vote};
|
use solana_vote_program::{
|
||||||
|
vote_instruction,
|
||||||
|
vote_state::{Vote, VoteStateVersions},
|
||||||
|
};
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::{thread::sleep, time::Duration};
|
use std::{thread::sleep, time::Duration};
|
||||||
|
@ -706,8 +709,10 @@ pub mod test {
|
||||||
for slot in *votes {
|
for slot in *votes {
|
||||||
vote_state.process_slot_vote_unchecked(*slot);
|
vote_state.process_slot_vote_unchecked(*slot);
|
||||||
}
|
}
|
||||||
vote_state
|
VoteState::serialize(
|
||||||
.serialize(&mut account.data)
|
&VoteStateVersions::Current(Box::new(vote_state)),
|
||||||
|
&mut account.data,
|
||||||
|
)
|
||||||
.expect("serialize state");
|
.expect("serialize state");
|
||||||
stakes.push((Pubkey::new_rand(), (*lamports, account)));
|
stakes.push((Pubkey::new_rand(), (*lamports, account)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1099,7 +1099,7 @@ pub(crate) mod tests {
|
||||||
transaction::TransactionError,
|
transaction::TransactionError,
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state;
|
use solana_stake_program::stake_state;
|
||||||
use solana_vote_program::vote_state::{self, Vote, VoteState};
|
use solana_vote_program::vote_state::{self, Vote, VoteState, VoteStateVersions};
|
||||||
use std::{
|
use std::{
|
||||||
fs::remove_dir_all,
|
fs::remove_dir_all,
|
||||||
iter,
|
iter,
|
||||||
|
@ -1134,7 +1134,8 @@ pub(crate) mod tests {
|
||||||
let mut vote_account = bank.get_account(&pubkey).unwrap();
|
let mut vote_account = bank.get_account(&pubkey).unwrap();
|
||||||
let mut vote_state = VoteState::from(&vote_account).unwrap();
|
let mut vote_state = VoteState::from(&vote_account).unwrap();
|
||||||
vote_state.process_slot_vote_unchecked(slot);
|
vote_state.process_slot_vote_unchecked(slot);
|
||||||
vote_state.to(&mut vote_account).unwrap();
|
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
||||||
|
VoteState::to(&versioned, &mut vote_account).unwrap();
|
||||||
bank.store_account(&pubkey, &vote_account);
|
bank.store_account(&pubkey, &vote_account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1718,7 +1719,8 @@ pub(crate) mod tests {
|
||||||
let mut leader_vote_account = bank.get_account(&pubkey).unwrap();
|
let mut leader_vote_account = bank.get_account(&pubkey).unwrap();
|
||||||
let mut vote_state = VoteState::from(&leader_vote_account).unwrap();
|
let mut vote_state = VoteState::from(&leader_vote_account).unwrap();
|
||||||
vote_state.process_slot_vote_unchecked(bank.slot());
|
vote_state.process_slot_vote_unchecked(bank.slot());
|
||||||
vote_state.to(&mut leader_vote_account).unwrap();
|
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
||||||
|
VoteState::to(&versioned, &mut leader_vote_account).unwrap();
|
||||||
bank.store_account(&pubkey, &leader_vote_account);
|
bank.store_account(&pubkey, &leader_vote_account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use solana_sdk::{
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
stake_history::{StakeHistory, StakeHistoryEntry},
|
stake_history::{StakeHistory, StakeHistoryEntry},
|
||||||
};
|
};
|
||||||
use solana_vote_program::vote_state::VoteState;
|
use solana_vote_program::vote_state::{VoteState, VoteStateVersions};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||||
|
@ -595,7 +595,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
||||||
let stake = Stake::new(
|
let stake = Stake::new(
|
||||||
self.lamports()?.saturating_sub(meta.rent_exempt_reserve), // can't stake the rent ;)
|
self.lamports()?.saturating_sub(meta.rent_exempt_reserve), // can't stake the rent ;)
|
||||||
vote_account.unsigned_key(),
|
vote_account.unsigned_key(),
|
||||||
&vote_account.state()?,
|
&State::<VoteStateVersions>::state(vote_account)?.convert_to_current(),
|
||||||
clock.epoch,
|
clock.epoch,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
@ -605,7 +605,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
||||||
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
||||||
stake.redelegate(
|
stake.redelegate(
|
||||||
vote_account.unsigned_key(),
|
vote_account.unsigned_key(),
|
||||||
&vote_account.state()?,
|
&State::<VoteStateVersions>::state(vote_account)?.convert_to_current(),
|
||||||
clock,
|
clock,
|
||||||
stake_history,
|
stake_history,
|
||||||
config,
|
config,
|
||||||
|
@ -778,7 +778,8 @@ pub fn redeem_rewards(
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
) -> Result<(u64, u64), InstructionError> {
|
) -> Result<(u64, u64), InstructionError> {
|
||||||
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
||||||
let vote_state = vote_account.state()?;
|
let vote_state: VoteState =
|
||||||
|
StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
||||||
|
|
||||||
if let Some((voters_reward, stakers_reward)) =
|
if let Some((voters_reward, stakers_reward)) =
|
||||||
stake.redeem_rewards(point_value, &vote_state, stake_history)
|
stake.redeem_rewards(point_value, &vote_state, stake_history)
|
||||||
|
@ -999,7 +1000,10 @@ mod tests {
|
||||||
100,
|
100,
|
||||||
));
|
));
|
||||||
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
||||||
vote_keyed_account.set_state(&vote_state).unwrap();
|
let vote_state_credits = vote_state.credits();
|
||||||
|
vote_keyed_account
|
||||||
|
.set_state(&VoteStateVersions::Current(Box::new(vote_state)))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let stake_pubkey = Pubkey::new_rand();
|
let stake_pubkey = Pubkey::new_rand();
|
||||||
let stake_lamports = 42;
|
let stake_lamports = 42;
|
||||||
|
@ -1057,7 +1061,7 @@ mod tests {
|
||||||
deactivation_epoch: std::u64::MAX,
|
deactivation_epoch: std::u64::MAX,
|
||||||
..Delegation::default()
|
..Delegation::default()
|
||||||
},
|
},
|
||||||
credits_observed: vote_state.credits(),
|
credits_observed: vote_state_credits,
|
||||||
..Stake::default()
|
..Stake::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1105,7 +1109,7 @@ mod tests {
|
||||||
deactivation_epoch: std::u64::MAX,
|
deactivation_epoch: std::u64::MAX,
|
||||||
..Delegation::default()
|
..Delegation::default()
|
||||||
},
|
},
|
||||||
credits_observed: vote_state.credits(),
|
credits_observed: vote_state_credits,
|
||||||
..Stake::default()
|
..Stake::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1535,7 +1539,9 @@ mod tests {
|
||||||
100,
|
100,
|
||||||
));
|
));
|
||||||
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
||||||
vote_keyed_account.set_state(&VoteState::default()).unwrap();
|
vote_keyed_account
|
||||||
|
.set_state(&VoteStateVersions::Current(Box::new(VoteState::default())))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.delegate(
|
stake_keyed_account.delegate(
|
||||||
&vote_keyed_account,
|
&vote_keyed_account,
|
||||||
|
@ -1624,7 +1630,9 @@ mod tests {
|
||||||
100,
|
100,
|
||||||
));
|
));
|
||||||
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
||||||
vote_keyed_account.set_state(&VoteState::default()).unwrap();
|
vote_keyed_account
|
||||||
|
.set_state(&VoteStateVersions::Current(Box::new(VoteState::default())))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
stake_keyed_account
|
stake_keyed_account
|
||||||
.delegate(
|
.delegate(
|
||||||
|
@ -1748,7 +1756,9 @@ mod tests {
|
||||||
100,
|
100,
|
||||||
));
|
));
|
||||||
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
||||||
vote_keyed_account.set_state(&VoteState::default()).unwrap();
|
vote_keyed_account
|
||||||
|
.set_state(&VoteStateVersions::Current(Box::new(VoteState::default())))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.delegate(
|
stake_keyed_account.delegate(
|
||||||
&vote_keyed_account,
|
&vote_keyed_account,
|
||||||
|
@ -1857,7 +1867,9 @@ mod tests {
|
||||||
100,
|
100,
|
||||||
));
|
));
|
||||||
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
let vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &vote_account);
|
||||||
vote_keyed_account.set_state(&VoteState::default()).unwrap();
|
vote_keyed_account
|
||||||
|
.set_state(&VoteStateVersions::Current(Box::new(VoteState::default())))
|
||||||
|
.unwrap();
|
||||||
let signers = vec![stake_pubkey].into_iter().collect();
|
let signers = vec![stake_pubkey].into_iter().collect();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.delegate(
|
stake_keyed_account.delegate(
|
||||||
|
|
|
@ -17,6 +17,7 @@ use solana_sdk::{
|
||||||
system_instruction,
|
system_instruction,
|
||||||
sysvar::{self, clock::Clock, slot_hashes::SlotHashes, Sysvar},
|
sysvar::{self, clock::Clock, slot_hashes::SlotHashes, Sysvar},
|
||||||
};
|
};
|
||||||
|
use std::collections::HashSet;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Reasons the stake might have had an error
|
/// Reasons the stake might have had an error
|
||||||
|
@ -187,7 +188,7 @@ pub fn process_instruction(
|
||||||
trace!("process_instruction: {:?}", data);
|
trace!("process_instruction: {:?}", data);
|
||||||
trace!("keyed_accounts: {:?}", keyed_accounts);
|
trace!("keyed_accounts: {:?}", keyed_accounts);
|
||||||
|
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
|
|
||||||
let keyed_accounts = &mut keyed_accounts.iter();
|
let keyed_accounts = &mut keyed_accounts.iter();
|
||||||
let me = &mut next_keyed_account(keyed_accounts)?;
|
let me = &mut next_keyed_account(keyed_accounts)?;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(clippy::implicit_hasher)]
|
|
||||||
//! Vote state, vote program
|
//! Vote state, vote program
|
||||||
//! Receive and processes votes from validators
|
//! Receive and processes votes from validators
|
||||||
use crate::authorized_voters::AuthorizedVoters;
|
use crate::authorized_voters::AuthorizedVoters;
|
||||||
|
@ -18,8 +17,13 @@ use solana_sdk::{
|
||||||
slot_hashes::SlotHash,
|
slot_hashes::SlotHash,
|
||||||
sysvar::clock::Clock,
|
sysvar::clock::Clock,
|
||||||
};
|
};
|
||||||
|
use std::boxed::Box;
|
||||||
use std::collections::{HashSet, VecDeque};
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
|
||||||
|
mod vote_state_0_23_5;
|
||||||
|
pub mod vote_state_versions;
|
||||||
|
pub use vote_state_versions::*;
|
||||||
|
|
||||||
// Maximum number of votes to keep around, tightly coupled with epoch_schedule::MIN_SLOTS_PER_EPOCH
|
// Maximum number of votes to keep around, tightly coupled with epoch_schedule::MIN_SLOTS_PER_EPOCH
|
||||||
pub const MAX_LOCKOUT_HISTORY: usize = 31;
|
pub const MAX_LOCKOUT_HISTORY: usize = 31;
|
||||||
pub const INITIAL_LOCKOUT: usize = 2;
|
pub const INITIAL_LOCKOUT: usize = 2;
|
||||||
|
@ -205,7 +209,7 @@ impl VoteState {
|
||||||
pub fn size_of() -> usize {
|
pub fn size_of() -> usize {
|
||||||
// Upper limit on the size of the Vote State. Equal to
|
// Upper limit on the size of the Vote State. Equal to
|
||||||
// size_of(VoteState) when votes.len() is MAX_LOCKOUT_HISTORY
|
// size_of(VoteState) when votes.len() is MAX_LOCKOUT_HISTORY
|
||||||
let vote_state = Self::get_max_sized_vote_state();
|
let vote_state = VoteStateVersions::Current(Box::new(Self::get_max_sized_vote_state()));
|
||||||
serialized_size(&vote_state).unwrap() as usize
|
serialized_size(&vote_state).unwrap() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,22 +219,26 @@ impl VoteState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility function, used by Stakes, tests
|
// utility function, used by Stakes, tests
|
||||||
pub fn to(&self, account: &mut Account) -> Option<()> {
|
pub fn to(versioned: &VoteStateVersions, account: &mut Account) -> Option<()> {
|
||||||
Self::serialize(self, &mut account.data).ok()
|
Self::serialize(versioned, &mut account.data).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
|
pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
|
||||||
deserialize(input).map_err(|_| InstructionError::InvalidAccountData)
|
deserialize::<VoteStateVersions>(&input)
|
||||||
|
.map(|versioned| versioned.convert_to_current())
|
||||||
|
.map_err(|_| InstructionError::InvalidAccountData)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize(&self, output: &mut [u8]) -> Result<(), InstructionError> {
|
pub fn serialize(
|
||||||
serialize_into(output, self).map_err(|err| match *err {
|
versioned: &VoteStateVersions,
|
||||||
|
output: &mut [u8],
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
serialize_into(output, versioned).map_err(|err| match *err {
|
||||||
ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
|
ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
|
||||||
_ => InstructionError::GenericError,
|
_ => InstructionError::GenericError,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility function, used by Stakes, tests
|
|
||||||
pub fn credits_from(account: &Account) -> Option<u64> {
|
pub fn credits_from(account: &Account) -> Option<u64> {
|
||||||
Self::from(account).map(|state| state.credits())
|
Self::from(account).map(|state| state.credits())
|
||||||
}
|
}
|
||||||
|
@ -540,14 +548,15 @@ impl VoteState {
|
||||||
/// Authorize the given pubkey to withdraw or 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
|
/// but will implicitly withdraw authorization from the previously authorized
|
||||||
/// key
|
/// key
|
||||||
pub fn authorize(
|
pub fn authorize<S: std::hash::BuildHasher>(
|
||||||
vote_account: &KeyedAccount,
|
vote_account: &KeyedAccount,
|
||||||
authorized: &Pubkey,
|
authorized: &Pubkey,
|
||||||
vote_authorize: VoteAuthorize,
|
vote_authorize: VoteAuthorize,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey, S>,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut vote_state: VoteState = vote_account.state()?;
|
let mut vote_state: VoteState =
|
||||||
|
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
||||||
|
|
||||||
// current authorized signer must say "yay"
|
// current authorized signer must say "yay"
|
||||||
match vote_authorize {
|
match vote_authorize {
|
||||||
|
@ -565,17 +574,18 @@ pub fn authorize(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vote_account.set_state(&vote_state)
|
vote_account.set_state(&VoteStateVersions::Current(Box::new(vote_state)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the node_pubkey, requires signature of the authorized voter
|
/// Update the node_pubkey, requires signature of the authorized voter
|
||||||
pub fn update_node(
|
pub fn update_node<S: std::hash::BuildHasher>(
|
||||||
vote_account: &KeyedAccount,
|
vote_account: &KeyedAccount,
|
||||||
node_pubkey: &Pubkey,
|
node_pubkey: &Pubkey,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey, S>,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut vote_state: VoteState = vote_account.state()?;
|
let mut vote_state: VoteState =
|
||||||
|
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
||||||
let authorized_voter = vote_state
|
let authorized_voter = vote_state
|
||||||
.get_and_update_authorized_voter(clock.epoch)
|
.get_and_update_authorized_voter(clock.epoch)
|
||||||
.expect("the clock epoch is monotonically increasing, so authorized voter must be known");
|
.expect("the clock epoch is monotonically increasing, so authorized voter must be known");
|
||||||
|
@ -585,12 +595,12 @@ pub fn update_node(
|
||||||
|
|
||||||
vote_state.node_pubkey = *node_pubkey;
|
vote_state.node_pubkey = *node_pubkey;
|
||||||
|
|
||||||
vote_account.set_state(&vote_state)
|
vote_account.set_state(&VoteStateVersions::Current(Box::new(vote_state)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_authorized_signer(
|
fn verify_authorized_signer<S: std::hash::BuildHasher>(
|
||||||
authorized: &Pubkey,
|
authorized: &Pubkey,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey, S>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if signers.contains(authorized) {
|
if signers.contains(authorized) {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -600,13 +610,14 @@ fn verify_authorized_signer(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Withdraw funds from the vote account
|
/// Withdraw funds from the vote account
|
||||||
pub fn withdraw(
|
pub fn withdraw<S: std::hash::BuildHasher>(
|
||||||
vote_account: &KeyedAccount,
|
vote_account: &KeyedAccount,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
to_account: &KeyedAccount,
|
to_account: &KeyedAccount,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey, S>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let vote_state: VoteState = vote_account.state()?;
|
let vote_state: VoteState =
|
||||||
|
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
||||||
|
|
||||||
verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
|
verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
|
||||||
|
|
||||||
|
@ -626,27 +637,31 @@ pub fn initialize_account(
|
||||||
vote_init: &VoteInit,
|
vote_init: &VoteInit,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let vote_state: VoteState = vote_account.state()?;
|
let versioned = State::<VoteStateVersions>::state(vote_account)?;
|
||||||
|
|
||||||
if !vote_state.authorized_voters.is_empty() {
|
if !versioned.is_uninitialized() {
|
||||||
return Err(InstructionError::AccountAlreadyInitialized);
|
return Err(InstructionError::AccountAlreadyInitialized);
|
||||||
}
|
}
|
||||||
vote_account.set_state(&VoteState::new(vote_init, clock))
|
|
||||||
|
vote_account.set_state(&VoteStateVersions::Current(Box::new(VoteState::new(
|
||||||
|
vote_init, clock,
|
||||||
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_vote(
|
pub fn process_vote<S: std::hash::BuildHasher>(
|
||||||
vote_account: &KeyedAccount,
|
vote_account: &KeyedAccount,
|
||||||
slot_hashes: &[SlotHash],
|
slot_hashes: &[SlotHash],
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
vote: &Vote,
|
vote: &Vote,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey, S>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut vote_state: VoteState = vote_account.state()?;
|
let versioned = State::<VoteStateVersions>::state(vote_account)?;
|
||||||
|
|
||||||
if vote_state.authorized_voters.is_empty() {
|
if versioned.is_uninitialized() {
|
||||||
return Err(InstructionError::UninitializedAccount);
|
return Err(InstructionError::UninitializedAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut vote_state = versioned.convert_to_current();
|
||||||
let authorized_voter = vote_state
|
let authorized_voter = vote_state
|
||||||
.get_and_update_authorized_voter(clock.epoch)
|
.get_and_update_authorized_voter(clock.epoch)
|
||||||
.expect("the clock epoch is monotonically increasinig, so authorized voter must be known");
|
.expect("the clock epoch is monotonically increasinig, so authorized voter must be known");
|
||||||
|
@ -660,7 +675,7 @@ pub fn process_vote(
|
||||||
.ok_or_else(|| VoteError::EmptySlots)
|
.ok_or_else(|| VoteError::EmptySlots)
|
||||||
.and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
|
.and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
|
||||||
}
|
}
|
||||||
vote_account.set_state(&vote_state)
|
vote_account.set_state(&VoteStateVersions::Current(Box::new(vote_state)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility function, used by Bank, tests
|
// utility function, used by Bank, tests
|
||||||
|
@ -672,7 +687,7 @@ pub fn create_account(
|
||||||
) -> Account {
|
) -> Account {
|
||||||
let mut vote_account = Account::new(lamports, VoteState::size_of(), &id());
|
let mut vote_account = Account::new(lamports, VoteState::size_of(), &id());
|
||||||
|
|
||||||
VoteState::new(
|
let vote_state = VoteState::new(
|
||||||
&VoteInit {
|
&VoteInit {
|
||||||
node_pubkey: *node_pubkey,
|
node_pubkey: *node_pubkey,
|
||||||
authorized_voter: *vote_pubkey,
|
authorized_voter: *vote_pubkey,
|
||||||
|
@ -680,9 +695,10 @@ pub fn create_account(
|
||||||
commission,
|
commission,
|
||||||
},
|
},
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
)
|
);
|
||||||
.to(&mut vote_account)
|
|
||||||
.unwrap();
|
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
||||||
|
VoteState::to(&versioned, &mut vote_account).unwrap();
|
||||||
|
|
||||||
vote_account
|
vote_account
|
||||||
}
|
}
|
||||||
|
@ -771,7 +787,7 @@ mod tests {
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
) -> Result<VoteState, InstructionError> {
|
) -> Result<VoteState, InstructionError> {
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
process_vote(
|
process_vote(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
slot_hashes,
|
slot_hashes,
|
||||||
|
@ -782,7 +798,8 @@ mod tests {
|
||||||
&vote.clone(),
|
&vote.clone(),
|
||||||
&signers,
|
&signers,
|
||||||
)?;
|
)?;
|
||||||
vote_account.borrow().state()
|
StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
|
||||||
|
.map(|versioned| versioned.convert_to_current())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// exercises all the keyed accounts stuff
|
/// exercises all the keyed accounts stuff
|
||||||
|
@ -807,16 +824,22 @@ mod tests {
|
||||||
vote_state
|
vote_state
|
||||||
.votes
|
.votes
|
||||||
.resize(MAX_LOCKOUT_HISTORY, Lockout::default());
|
.resize(MAX_LOCKOUT_HISTORY, Lockout::default());
|
||||||
assert!(vote_state.serialize(&mut buffer[0..4]).is_err());
|
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
||||||
vote_state.serialize(&mut buffer).unwrap();
|
assert!(VoteState::serialize(&versioned, &mut buffer[0..4]).is_err());
|
||||||
assert_eq!(VoteState::deserialize(&buffer).unwrap(), vote_state);
|
VoteState::serialize(&versioned, &mut buffer).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
VoteStateVersions::Current(Box::new(VoteState::deserialize(&buffer).unwrap())),
|
||||||
|
versioned
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_voter_registration() {
|
fn test_voter_registration() {
|
||||||
let (vote_pubkey, vote_account) = create_test_account();
|
let (vote_pubkey, vote_account) = create_test_account();
|
||||||
|
|
||||||
let vote_state: VoteState = vote_account.borrow().state().unwrap();
|
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
assert_eq!(vote_state.authorized_voters.len(), 1);
|
assert_eq!(vote_state.authorized_voters.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*vote_state.authorized_voters.first().unwrap().1,
|
*vote_state.authorized_voters.first().unwrap().1,
|
||||||
|
@ -878,7 +901,7 @@ mod tests {
|
||||||
let node_pubkey = Pubkey::new_rand();
|
let node_pubkey = Pubkey::new_rand();
|
||||||
|
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = update_node(
|
let res = update_node(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&node_pubkey,
|
&node_pubkey,
|
||||||
|
@ -886,11 +909,13 @@ mod tests {
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
|
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
|
||||||
let vote_state: VoteState = vote_account.borrow().state().unwrap();
|
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
assert!(vote_state.node_pubkey != node_pubkey);
|
assert!(vote_state.node_pubkey != node_pubkey);
|
||||||
|
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = update_node(
|
let res = update_node(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&node_pubkey,
|
&node_pubkey,
|
||||||
|
@ -898,16 +923,20 @@ mod tests {
|
||||||
&Clock::default(),
|
&Clock::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(res, Ok(()));
|
assert_eq!(res, Ok(()));
|
||||||
let vote_state: VoteState = vote_account.borrow().state().unwrap();
|
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
assert_eq!(vote_state.node_pubkey, node_pubkey);
|
assert_eq!(vote_state.node_pubkey, node_pubkey);
|
||||||
|
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let mut clock = Clock::default();
|
let mut clock = Clock::default();
|
||||||
clock.epoch += 10;
|
clock.epoch += 10;
|
||||||
let res = update_node(&keyed_accounts[0], &node_pubkey, &signers, &clock);
|
let res = update_node(&keyed_accounts[0], &node_pubkey, &signers, &clock);
|
||||||
assert_eq!(res, Ok(()));
|
assert_eq!(res, Ok(()));
|
||||||
let vote_state: VoteState = vote_account.borrow().state().unwrap();
|
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
assert_eq!(vote_state.node_pubkey, node_pubkey);
|
assert_eq!(vote_state.node_pubkey, node_pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -918,7 +947,7 @@ mod tests {
|
||||||
|
|
||||||
// unsigned
|
// unsigned
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = process_vote(
|
let res = process_vote(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&[(*vote.slots.last().unwrap(), vote.hash)],
|
&[(*vote.slots.last().unwrap(), vote.hash)],
|
||||||
|
@ -934,7 +963,7 @@ mod tests {
|
||||||
|
|
||||||
// signed
|
// signed
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = process_vote(
|
let res = process_vote(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&[(*vote.slots.last().unwrap(), vote.hash)],
|
&[(*vote.slots.last().unwrap(), vote.hash)],
|
||||||
|
@ -950,7 +979,7 @@ mod tests {
|
||||||
|
|
||||||
// another voter, unsigned
|
// another voter, unsigned
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let authorized_voter_pubkey = Pubkey::new_rand();
|
let authorized_voter_pubkey = Pubkey::new_rand();
|
||||||
let res = authorize(
|
let res = authorize(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
|
@ -966,7 +995,7 @@ mod tests {
|
||||||
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
|
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
|
||||||
|
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = authorize(
|
let res = authorize(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&authorized_voter_pubkey,
|
&authorized_voter_pubkey,
|
||||||
|
@ -981,7 +1010,7 @@ mod tests {
|
||||||
assert_eq!(res, Ok(()));
|
assert_eq!(res, Ok(()));
|
||||||
|
|
||||||
// Already set an authorized voter earlier for leader_schedule_epoch == 2
|
// Already set an authorized voter earlier for leader_schedule_epoch == 2
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = authorize(
|
let res = authorize(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&authorized_voter_pubkey,
|
&authorized_voter_pubkey,
|
||||||
|
@ -1001,7 +1030,7 @@ mod tests {
|
||||||
KeyedAccount::new(&vote_pubkey, false, &vote_account),
|
KeyedAccount::new(&vote_pubkey, false, &vote_account),
|
||||||
KeyedAccount::new(&authorized_voter_pubkey, true, &authorized_voter_account),
|
KeyedAccount::new(&authorized_voter_pubkey, true, &authorized_voter_account),
|
||||||
];
|
];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = authorize(
|
let res = authorize(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&authorized_voter_pubkey,
|
&authorized_voter_pubkey,
|
||||||
|
@ -1020,7 +1049,7 @@ mod tests {
|
||||||
// authorize another withdrawer
|
// authorize another withdrawer
|
||||||
// another voter
|
// another voter
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let authorized_withdrawer_pubkey = Pubkey::new_rand();
|
let authorized_withdrawer_pubkey = Pubkey::new_rand();
|
||||||
let res = authorize(
|
let res = authorize(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
|
@ -1041,7 +1070,7 @@ mod tests {
|
||||||
KeyedAccount::new(&vote_pubkey, false, &vote_account),
|
KeyedAccount::new(&vote_pubkey, false, &vote_account),
|
||||||
KeyedAccount::new(&authorized_withdrawer_pubkey, true, &withdrawer_account),
|
KeyedAccount::new(&authorized_withdrawer_pubkey, true, &withdrawer_account),
|
||||||
];
|
];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = authorize(
|
let res = authorize(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&authorized_withdrawer_pubkey,
|
&authorized_withdrawer_pubkey,
|
||||||
|
@ -1057,7 +1086,7 @@ mod tests {
|
||||||
|
|
||||||
// not signed by authorized voter
|
// not signed by authorized voter
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let vote = Vote::new(vec![2], Hash::default());
|
let vote = Vote::new(vec![2], Hash::default());
|
||||||
let res = process_vote(
|
let res = process_vote(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
|
@ -1078,7 +1107,7 @@ mod tests {
|
||||||
KeyedAccount::new(&vote_pubkey, false, &vote_account),
|
KeyedAccount::new(&vote_pubkey, false, &vote_account),
|
||||||
KeyedAccount::new(&authorized_voter_pubkey, true, &authorized_voter_account),
|
KeyedAccount::new(&authorized_voter_pubkey, true, &authorized_voter_account),
|
||||||
];
|
];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let vote = Vote::new(vec![2], Hash::default());
|
let vote = Vote::new(vec![2], Hash::default());
|
||||||
let res = process_vote(
|
let res = process_vote(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
|
@ -1111,7 +1140,10 @@ mod tests {
|
||||||
fn test_vote_lockout() {
|
fn test_vote_lockout() {
|
||||||
let (_vote_pubkey, vote_account) = create_test_account();
|
let (_vote_pubkey, vote_account) = create_test_account();
|
||||||
|
|
||||||
let mut vote_state: VoteState = vote_account.borrow().state().unwrap();
|
let mut vote_state: VoteState =
|
||||||
|
StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
|
|
||||||
for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
|
for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
|
||||||
vote_state.process_slot_vote_unchecked((INITIAL_LOCKOUT as usize * i) as u64);
|
vote_state.process_slot_vote_unchecked((INITIAL_LOCKOUT as usize * i) as u64);
|
||||||
|
@ -1421,7 +1453,7 @@ mod tests {
|
||||||
|
|
||||||
// unsigned request
|
// unsigned request
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, false, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = withdraw(
|
let res = withdraw(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
0,
|
0,
|
||||||
|
@ -1436,7 +1468,7 @@ mod tests {
|
||||||
|
|
||||||
// insufficient funds
|
// insufficient funds
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = withdraw(
|
let res = withdraw(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
101,
|
101,
|
||||||
|
@ -1453,7 +1485,7 @@ mod tests {
|
||||||
let to_account = RefCell::new(Account::default());
|
let to_account = RefCell::new(Account::default());
|
||||||
let lamports = vote_account.borrow().lamports;
|
let lamports = vote_account.borrow().lamports;
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = withdraw(
|
let res = withdraw(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
lamports,
|
lamports,
|
||||||
|
@ -1470,7 +1502,7 @@ mod tests {
|
||||||
// authorize authorized_withdrawer
|
// authorize authorized_withdrawer
|
||||||
let authorized_withdrawer_pubkey = Pubkey::new_rand();
|
let authorized_withdrawer_pubkey = Pubkey::new_rand();
|
||||||
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
let keyed_accounts = &[KeyedAccount::new(&vote_pubkey, true, &vote_account)];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let res = authorize(
|
let res = authorize(
|
||||||
&keyed_accounts[0],
|
&keyed_accounts[0],
|
||||||
&authorized_withdrawer_pubkey,
|
&authorized_withdrawer_pubkey,
|
||||||
|
@ -1486,7 +1518,7 @@ mod tests {
|
||||||
KeyedAccount::new(&vote_pubkey, false, &vote_account),
|
KeyedAccount::new(&vote_pubkey, false, &vote_account),
|
||||||
KeyedAccount::new(&authorized_withdrawer_pubkey, true, &withdrawer_account),
|
KeyedAccount::new(&authorized_withdrawer_pubkey, true, &withdrawer_account),
|
||||||
];
|
];
|
||||||
let signers = get_signers(keyed_accounts);
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
let keyed_accounts = &mut keyed_accounts.iter();
|
let keyed_accounts = &mut keyed_accounts.iter();
|
||||||
let vote_keyed_account = next_keyed_account(keyed_accounts).unwrap();
|
let vote_keyed_account = next_keyed_account(keyed_accounts).unwrap();
|
||||||
let withdrawer_keyed_account = next_keyed_account(keyed_accounts).unwrap();
|
let withdrawer_keyed_account = next_keyed_account(keyed_accounts).unwrap();
|
||||||
|
@ -1802,21 +1834,25 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vote_state_max_size() {
|
fn test_vote_state_max_size() {
|
||||||
let mut max_sized_data = vec![0; VoteState::size_of()];
|
let mut max_sized_data = vec![0; VoteState::size_of()];
|
||||||
let mut vote_state = VoteState::get_max_sized_vote_state();
|
let vote_state = VoteState::get_max_sized_vote_state();
|
||||||
let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
|
let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
|
||||||
let start_current_epoch =
|
let start_current_epoch =
|
||||||
start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
|
start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
|
||||||
|
|
||||||
|
let mut vote_state = Some(vote_state);
|
||||||
for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
|
for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
|
||||||
vote_state
|
vote_state.as_mut().map(|vote_state| {
|
||||||
.set_new_authorized_voter(
|
vote_state.set_new_authorized_voter(
|
||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
i,
|
i,
|
||||||
i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
|
i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
|
||||||
|_| Ok(()),
|
|_| Ok(()),
|
||||||
)
|
)
|
||||||
.unwrap();
|
});
|
||||||
vote_state.serialize(&mut max_sized_data).unwrap();
|
|
||||||
|
let versioned = VoteStateVersions::Current(Box::new(vote_state.take().unwrap()));
|
||||||
|
VoteState::serialize(&versioned, &mut max_sized_data).unwrap();
|
||||||
|
vote_state = Some(versioned.convert_to_current());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const MAX_ITEMS: usize = 32;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
|
pub struct VoteState0_23_5 {
|
||||||
|
/// the node that votes in this account
|
||||||
|
pub node_pubkey: Pubkey,
|
||||||
|
|
||||||
|
/// the signer for vote transactions
|
||||||
|
pub authorized_voter: Pubkey,
|
||||||
|
/// when the authorized voter was set/initialized
|
||||||
|
pub authorized_voter_epoch: Epoch,
|
||||||
|
|
||||||
|
/// history of prior authorized voters and the epoch ranges for which
|
||||||
|
/// they were set
|
||||||
|
pub prior_voters: CircBuf<(Pubkey, Epoch, Epoch, Slot)>,
|
||||||
|
|
||||||
|
/// the signer for withdrawals
|
||||||
|
pub authorized_withdrawer: Pubkey,
|
||||||
|
/// percentage (0-100) 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>,
|
||||||
|
|
||||||
|
/// history of how many credits earned by the end of each epoch
|
||||||
|
/// each tuple is (Epoch, credits, prev_credits)
|
||||||
|
pub epoch_credits: Vec<(Epoch, u64, u64)>,
|
||||||
|
|
||||||
|
/// most recent timestamp submitted with a vote
|
||||||
|
pub last_timestamp: BlockTimestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
|
pub struct CircBuf<I> {
|
||||||
|
pub buf: [I; MAX_ITEMS],
|
||||||
|
/// next pointer
|
||||||
|
pub idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Default + Copy> Default for CircBuf<I> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
buf: [I::default(); MAX_ITEMS],
|
||||||
|
idx: MAX_ITEMS - 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> CircBuf<I> {
|
||||||
|
pub fn append(&mut self, item: I) {
|
||||||
|
// remember prior delegate and when we switched, to support later slashing
|
||||||
|
self.idx += 1;
|
||||||
|
self.idx %= MAX_ITEMS;
|
||||||
|
|
||||||
|
self.buf[self.idx] = item;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
use super::*;
|
||||||
|
use crate::vote_state::vote_state_0_23_5::VoteState0_23_5;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
|
pub enum VoteStateVersions {
|
||||||
|
V0_23_5(Box<VoteState0_23_5>),
|
||||||
|
Current(Box<VoteState>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VoteStateVersions {
|
||||||
|
pub fn convert_to_current(self) -> VoteState {
|
||||||
|
match self {
|
||||||
|
VoteStateVersions::V0_23_5(state) => {
|
||||||
|
let authorized_voters =
|
||||||
|
AuthorizedVoters::new(state.authorized_voter_epoch, state.authorized_voter);
|
||||||
|
|
||||||
|
VoteState {
|
||||||
|
node_pubkey: state.node_pubkey,
|
||||||
|
|
||||||
|
/// the signer for withdrawals
|
||||||
|
authorized_withdrawer: state.authorized_withdrawer,
|
||||||
|
|
||||||
|
/// percentage (0-100) that represents what part of a rewards
|
||||||
|
/// payout should be given to this VoteAccount
|
||||||
|
commission: state.commission,
|
||||||
|
|
||||||
|
votes: state.votes.clone(),
|
||||||
|
|
||||||
|
root_slot: state.root_slot,
|
||||||
|
|
||||||
|
/// the signer for vote transactions
|
||||||
|
authorized_voters,
|
||||||
|
|
||||||
|
/// history of prior authorized voters and the epochs for which
|
||||||
|
/// they were set, the bottom end of the range is inclusive,
|
||||||
|
/// the top of the range is exclusive
|
||||||
|
prior_voters: CircBuf::default(),
|
||||||
|
|
||||||
|
/// history of how many credits earned by the end of each epoch
|
||||||
|
/// each tuple is (Epoch, credits, prev_credits)
|
||||||
|
epoch_credits: state.epoch_credits.clone(),
|
||||||
|
|
||||||
|
/// most recent timestamp submitted with a vote
|
||||||
|
last_timestamp: state.last_timestamp.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VoteStateVersions::Current(state) => *state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_uninitialized(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
VoteStateVersions::V0_23_5(vote_state) => {
|
||||||
|
vote_state.authorized_voter == Pubkey::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
VoteStateVersions::Current(vote_state) => vote_state.authorized_voters.is_empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2172,6 +2172,7 @@ mod tests {
|
||||||
stake_instruction,
|
stake_instruction,
|
||||||
stake_state::{self, Authorized, Delegation, Lockup, Stake},
|
stake_state::{self, Authorized, Delegation, Lockup, Stake},
|
||||||
};
|
};
|
||||||
|
use solana_vote_program::vote_state::VoteStateVersions;
|
||||||
use solana_vote_program::{
|
use solana_vote_program::{
|
||||||
vote_instruction,
|
vote_instruction,
|
||||||
vote_state::{self, Vote, VoteInit, VoteState, MAX_LOCKOUT_HISTORY},
|
vote_state::{self, Vote, VoteInit, VoteState, MAX_LOCKOUT_HISTORY},
|
||||||
|
@ -3069,11 +3070,20 @@ mod tests {
|
||||||
bank.store_account(&archiver_id, &archiver_account);
|
bank.store_account(&archiver_id, &archiver_account);
|
||||||
|
|
||||||
// generate some rewards
|
// generate some rewards
|
||||||
let mut vote_state = VoteState::from(&vote_account).unwrap();
|
let mut vote_state = Some(VoteState::from(&vote_account).unwrap());
|
||||||
for i in 0..MAX_LOCKOUT_HISTORY + 42 {
|
for i in 0..MAX_LOCKOUT_HISTORY + 42 {
|
||||||
vote_state.process_slot_vote_unchecked(i as u64);
|
vote_state
|
||||||
vote_state.to(&mut vote_account).unwrap();
|
.as_mut()
|
||||||
|
.map(|v| v.process_slot_vote_unchecked(i as u64));
|
||||||
|
let versioned = VoteStateVersions::Current(Box::new(vote_state.take().unwrap()));
|
||||||
|
VoteState::to(&versioned, &mut vote_account).unwrap();
|
||||||
bank.store_account(&vote_id, &vote_account);
|
bank.store_account(&vote_id, &vote_account);
|
||||||
|
match versioned {
|
||||||
|
VoteStateVersions::Current(v) => {
|
||||||
|
vote_state = Some(*v);
|
||||||
|
}
|
||||||
|
_ => panic!("Has to be of type Current"),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
bank.store_account(&vote_id, &vote_account);
|
bank.store_account(&vote_id, &vote_account);
|
||||||
|
|
||||||
|
|
|
@ -195,7 +195,9 @@ pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use solana_sdk::{pubkey::Pubkey, rent::Rent};
|
use solana_sdk::{pubkey::Pubkey, rent::Rent};
|
||||||
use solana_stake_program::stake_state;
|
use solana_stake_program::stake_state;
|
||||||
use solana_vote_program::vote_state::{self, VoteState, MAX_LOCKOUT_HISTORY};
|
use solana_vote_program::vote_state::{
|
||||||
|
self, VoteState, VoteStateVersions, MAX_LOCKOUT_HISTORY,
|
||||||
|
};
|
||||||
|
|
||||||
// set up some dummies for a staked node (( vote ) ( stake ))
|
// set up some dummies for a staked node (( vote ) ( stake ))
|
||||||
pub fn create_staked_node_accounts(stake: u64) -> ((Pubkey, Account), (Pubkey, Account)) {
|
pub fn create_staked_node_accounts(stake: u64) -> ((Pubkey, Account), (Pubkey, Account)) {
|
||||||
|
@ -319,31 +321,55 @@ pub mod tests {
|
||||||
assert_eq!(stakes.points(), 0);
|
assert_eq!(stakes.points(), 0);
|
||||||
assert_eq!(stakes.claim_points(), 0);
|
assert_eq!(stakes.claim_points(), 0);
|
||||||
|
|
||||||
let mut vote_state = VoteState::from(&vote_account).unwrap();
|
let mut vote_state = Some(VoteState::from(&vote_account).unwrap());
|
||||||
for i in 0..MAX_LOCKOUT_HISTORY + 42 {
|
for i in 0..MAX_LOCKOUT_HISTORY + 42 {
|
||||||
vote_state.process_slot_vote_unchecked(i as u64);
|
vote_state
|
||||||
vote_state.to(&mut vote_account).unwrap();
|
.as_mut()
|
||||||
|
.map(|v| v.process_slot_vote_unchecked(i as u64));
|
||||||
|
let versioned = VoteStateVersions::Current(Box::new(vote_state.take().unwrap()));
|
||||||
|
VoteState::to(&versioned, &mut vote_account).unwrap();
|
||||||
|
match versioned {
|
||||||
|
VoteStateVersions::Current(v) => {
|
||||||
|
vote_state = Some(*v);
|
||||||
|
}
|
||||||
|
_ => panic!("Has to be of type Current"),
|
||||||
|
};
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account);
|
||||||
assert_eq!(stakes.points(), vote_state.credits() * stake);
|
assert_eq!(
|
||||||
|
stakes.points(),
|
||||||
|
vote_state.as_ref().unwrap().credits() * stake
|
||||||
|
);
|
||||||
}
|
}
|
||||||
vote_account.lamports = 0;
|
vote_account.lamports = 0;
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account);
|
||||||
assert_eq!(stakes.points(), vote_state.credits() * stake);
|
assert_eq!(
|
||||||
|
stakes.points(),
|
||||||
|
vote_state.as_ref().unwrap().credits() * stake
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(stakes.claim_points(), vote_state.credits() * stake);
|
assert_eq!(
|
||||||
|
stakes.claim_points(),
|
||||||
|
vote_state.as_ref().unwrap().credits() * stake
|
||||||
|
);
|
||||||
assert_eq!(stakes.claim_points(), 0);
|
assert_eq!(stakes.claim_points(), 0);
|
||||||
assert_eq!(stakes.claim_points(), 0);
|
assert_eq!(stakes.claim_points(), 0);
|
||||||
|
|
||||||
// points come out of nowhere, but don't care here ;)
|
// points come out of nowhere, but don't care here ;)
|
||||||
vote_account.lamports = 1;
|
vote_account.lamports = 1;
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account);
|
||||||
assert_eq!(stakes.points(), vote_state.credits() * stake);
|
assert_eq!(
|
||||||
|
stakes.points(),
|
||||||
|
vote_state.as_ref().unwrap().credits() * stake
|
||||||
|
);
|
||||||
|
|
||||||
// test going backwards, should never go backwards
|
// test going backwards, should never go backwards
|
||||||
let old_vote_state = vote_state;
|
let old_vote_state = vote_state;
|
||||||
let vote_account = vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 1);
|
let vote_account = vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 1);
|
||||||
stakes.store(&vote_pubkey, &vote_account);
|
stakes.store(&vote_pubkey, &vote_account);
|
||||||
assert_eq!(stakes.points(), old_vote_state.credits() * stake);
|
assert_eq!(
|
||||||
|
stakes.points(),
|
||||||
|
old_vote_state.as_ref().unwrap().credits() * stake
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -18,7 +18,7 @@ use solana_stake_program::{
|
||||||
};
|
};
|
||||||
use solana_vote_program::{
|
use solana_vote_program::{
|
||||||
vote_instruction,
|
vote_instruction,
|
||||||
vote_state::{Vote, VoteInit, VoteState},
|
vote_state::{Vote, VoteInit, VoteState, VoteStateVersions},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -254,7 +254,9 @@ fn test_stake_account_lifetime() {
|
||||||
|
|
||||||
// Test that votes and credits are there
|
// Test that votes and credits are there
|
||||||
let account = bank.get_account(&vote_pubkey).expect("account not found");
|
let account = bank.get_account(&vote_pubkey).expect("account not found");
|
||||||
let vote_state: VoteState = account.state().expect("couldn't unpack account data");
|
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&account)
|
||||||
|
.expect("couldn't unpack account data")
|
||||||
|
.convert_to_current();
|
||||||
|
|
||||||
// 1 less vote, as the first vote should have cleared the lockout
|
// 1 less vote, as the first vote should have cleared the lockout
|
||||||
assert_eq!(vote_state.votes.len(), 31);
|
assert_eq!(vote_state.votes.len(), 31);
|
||||||
|
|
Loading…
Reference in New Issue