2019-05-16 08:23:31 -07:00
|
|
|
//! Stakes serve as a cache of stake and vote accounts to derive
|
|
|
|
//! node stakes
|
2020-01-28 17:03:20 -08:00
|
|
|
use solana_sdk::{
|
|
|
|
account::Account, clock::Epoch, pubkey::Pubkey, sysvar::stake_history::StakeHistory,
|
|
|
|
};
|
2019-11-25 13:14:32 -08:00
|
|
|
use solana_stake_program::stake_state::{new_stake_history_entry, Delegation, StakeState};
|
2019-11-20 10:12:43 -08:00
|
|
|
use solana_vote_program::vote_state::VoteState;
|
2019-05-30 21:31:35 -07:00
|
|
|
use std::collections::HashMap;
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-30 21:31:35 -07:00
|
|
|
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize)]
|
2019-05-16 08:23:31 -07:00
|
|
|
pub struct Stakes {
|
|
|
|
/// vote accounts
|
|
|
|
vote_accounts: HashMap<Pubkey, (u64, Account)>,
|
|
|
|
|
2019-11-25 13:14:32 -08:00
|
|
|
/// stake_delegations
|
|
|
|
stake_delegations: HashMap<Pubkey, Delegation>,
|
2019-06-14 11:38:37 -07:00
|
|
|
|
|
|
|
/// unclaimed points.
|
|
|
|
// a point is a credit multiplied by the stake
|
|
|
|
points: u64,
|
2019-06-19 11:54:52 -07:00
|
|
|
|
|
|
|
/// current epoch, used to calculate current stake
|
2019-07-31 15:13:26 -07:00
|
|
|
epoch: Epoch,
|
2019-08-12 20:59:57 -07:00
|
|
|
|
|
|
|
/// history of staking levels
|
|
|
|
stake_history: StakeHistory,
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Stakes {
|
2019-08-12 20:59:57 -07:00
|
|
|
pub fn history(&self) -> &StakeHistory {
|
|
|
|
&self.stake_history
|
|
|
|
}
|
2019-07-31 15:13:26 -07:00
|
|
|
pub fn clone_with_epoch(&self, epoch: Epoch) -> Self {
|
2019-06-19 11:54:52 -07:00
|
|
|
if self.epoch == epoch {
|
|
|
|
self.clone()
|
|
|
|
} else {
|
2019-08-12 20:59:57 -07:00
|
|
|
let mut stake_history = self.stake_history.clone();
|
|
|
|
|
|
|
|
stake_history.add(
|
|
|
|
self.epoch,
|
|
|
|
new_stake_history_entry(
|
|
|
|
self.epoch,
|
2019-11-25 13:14:32 -08:00
|
|
|
self.stake_delegations
|
2019-08-12 20:59:57 -07:00
|
|
|
.iter()
|
2019-11-25 13:14:32 -08:00
|
|
|
.map(|(_pubkey, stake_delegation)| stake_delegation),
|
2019-08-12 20:59:57 -07:00
|
|
|
Some(&self.stake_history),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2019-06-19 11:54:52 -07:00
|
|
|
Stakes {
|
2019-11-25 13:14:32 -08:00
|
|
|
stake_delegations: self.stake_delegations.clone(),
|
2019-06-19 11:54:52 -07:00
|
|
|
points: self.points,
|
|
|
|
epoch,
|
|
|
|
vote_accounts: self
|
|
|
|
.vote_accounts
|
|
|
|
.iter()
|
|
|
|
.map(|(pubkey, (_stake, account))| {
|
|
|
|
(
|
|
|
|
*pubkey,
|
2019-08-12 20:59:57 -07:00
|
|
|
(
|
|
|
|
self.calculate_stake(pubkey, epoch, Some(&stake_history)),
|
|
|
|
account.clone(),
|
|
|
|
),
|
2019-06-19 11:54:52 -07:00
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect(),
|
2019-08-12 20:59:57 -07:00
|
|
|
stake_history,
|
2019-06-19 11:54:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
// sum the stakes that point to the given voter_pubkey
|
2019-08-12 20:59:57 -07:00
|
|
|
fn calculate_stake(
|
|
|
|
&self,
|
|
|
|
voter_pubkey: &Pubkey,
|
|
|
|
epoch: Epoch,
|
|
|
|
stake_history: Option<&StakeHistory>,
|
|
|
|
) -> u64 {
|
2019-11-25 13:14:32 -08:00
|
|
|
self.stake_delegations
|
2019-05-16 08:23:31 -07:00
|
|
|
.iter()
|
2019-11-25 13:14:32 -08:00
|
|
|
.map(|(_, stake_delegation)| {
|
|
|
|
if &stake_delegation.voter_pubkey == voter_pubkey {
|
|
|
|
stake_delegation.stake(epoch, stake_history)
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
})
|
|
|
|
.sum()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_stake(account: &Account) -> bool {
|
2019-11-20 10:12:43 -08:00
|
|
|
solana_vote_program::check_id(&account.owner)
|
|
|
|
|| solana_stake_program::check_id(&account.owner)
|
2019-07-31 15:13:26 -07:00
|
|
|
&& account.data.len() >= std::mem::size_of::<StakeState>()
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn store(&mut self, pubkey: &Pubkey, account: &Account) {
|
2019-11-20 10:12:43 -08:00
|
|
|
if solana_vote_program::check_id(&account.owner) {
|
2019-05-16 08:23:31 -07:00
|
|
|
if account.lamports == 0 {
|
|
|
|
self.vote_accounts.remove(pubkey);
|
|
|
|
} else {
|
2019-06-14 11:38:37 -07:00
|
|
|
let old = self.vote_accounts.get(pubkey);
|
|
|
|
|
2019-08-12 20:59:57 -07:00
|
|
|
let stake = old.map_or_else(
|
|
|
|
|| self.calculate_stake(pubkey, self.epoch, Some(&self.stake_history)),
|
|
|
|
|v| v.0,
|
|
|
|
);
|
2019-06-14 11:38:37 -07:00
|
|
|
|
|
|
|
// count any increase in points, can only go forward
|
|
|
|
let old_credits = old
|
|
|
|
.and_then(|(_stake, old_account)| VoteState::credits_from(old_account))
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
let credits = VoteState::credits_from(account).unwrap_or(old_credits);
|
|
|
|
|
|
|
|
self.points += credits.saturating_sub(old_credits) * stake;
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
self.vote_accounts.insert(*pubkey, (stake, account.clone()));
|
|
|
|
}
|
2019-11-20 10:12:43 -08:00
|
|
|
} else if solana_stake_program::check_id(&account.owner) {
|
2019-05-23 23:20:04 -07:00
|
|
|
// old_stake is stake lamports and voter_pubkey from the pre-store() version
|
2019-11-25 13:14:32 -08:00
|
|
|
let old_stake = self.stake_delegations.get(pubkey).map(|delegation| {
|
|
|
|
(
|
|
|
|
delegation.voter_pubkey,
|
|
|
|
delegation.stake(self.epoch, Some(&self.stake_history)),
|
|
|
|
)
|
2019-06-19 11:54:52 -07:00
|
|
|
});
|
|
|
|
|
2019-11-25 13:14:32 -08:00
|
|
|
let delegation = StakeState::delegation_from(account);
|
|
|
|
|
|
|
|
let stake = delegation.map(|delegation| {
|
2019-06-19 11:54:52 -07:00
|
|
|
(
|
2019-11-25 13:14:32 -08:00
|
|
|
delegation.voter_pubkey,
|
2019-06-19 11:54:52 -07:00
|
|
|
if account.lamports != 0 {
|
2019-11-25 13:14:32 -08:00
|
|
|
delegation.stake(self.epoch, Some(&self.stake_history))
|
2019-06-19 11:54:52 -07:00
|
|
|
} else {
|
|
|
|
0
|
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
// if adjustments need to be made...
|
|
|
|
if stake != old_stake {
|
2019-06-19 11:54:52 -07:00
|
|
|
if let Some((voter_pubkey, stake)) = old_stake {
|
2019-05-16 08:23:31 -07:00
|
|
|
self.vote_accounts
|
2019-06-19 11:54:52 -07:00
|
|
|
.entry(voter_pubkey)
|
|
|
|
.and_modify(|e| e.0 -= stake);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
2019-06-10 16:17:29 -07:00
|
|
|
if let Some((voter_pubkey, stake)) = stake {
|
2019-05-16 08:23:31 -07:00
|
|
|
self.vote_accounts
|
2019-05-23 23:20:04 -07:00
|
|
|
.entry(voter_pubkey)
|
2019-05-16 08:23:31 -07:00
|
|
|
.and_modify(|e| e.0 += stake);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if account.lamports == 0 {
|
2019-11-25 13:14:32 -08:00
|
|
|
self.stake_delegations.remove(pubkey);
|
|
|
|
} else if let Some(delegation) = delegation {
|
|
|
|
self.stake_delegations.insert(*pubkey, delegation);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-30 21:57:49 -04:00
|
|
|
|
2019-05-16 08:23:31 -07:00
|
|
|
pub fn vote_accounts(&self) -> &HashMap<Pubkey, (u64, Account)> {
|
|
|
|
&self.vote_accounts
|
|
|
|
}
|
2019-06-11 11:44:58 -07:00
|
|
|
|
2019-12-08 20:52:01 -08:00
|
|
|
pub fn stake_delegations(&self) -> &HashMap<Pubkey, Delegation> {
|
|
|
|
&self.stake_delegations
|
|
|
|
}
|
|
|
|
|
2019-06-11 11:44:58 -07:00
|
|
|
pub fn highest_staked_node(&self) -> Option<Pubkey> {
|
|
|
|
self.vote_accounts
|
|
|
|
.iter()
|
|
|
|
.max_by(|(_ak, av), (_bk, bv)| av.0.cmp(&bv.0))
|
|
|
|
.and_then(|(_k, (_stake, account))| VoteState::from(account))
|
|
|
|
.map(|vote_state| vote_state.node_pubkey)
|
|
|
|
}
|
2019-06-14 11:38:37 -07:00
|
|
|
|
|
|
|
/// currently unclaimed points
|
2019-06-20 12:22:29 -07:00
|
|
|
pub fn points(&self) -> u64 {
|
2019-06-14 11:38:37 -07:00
|
|
|
self.points
|
|
|
|
}
|
|
|
|
|
|
|
|
/// "claims" points, resets points to 0
|
|
|
|
pub fn claim_points(&mut self) -> u64 {
|
|
|
|
let points = self.points;
|
|
|
|
self.points = 0;
|
|
|
|
points
|
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2019-06-14 11:38:37 -07:00
|
|
|
pub mod tests {
|
2019-05-16 08:23:31 -07:00
|
|
|
use super::*;
|
2019-11-12 12:33:40 -08:00
|
|
|
use solana_sdk::{pubkey::Pubkey, rent::Rent};
|
2019-11-20 10:12:43 -08:00
|
|
|
use solana_stake_program::stake_state;
|
2020-02-25 17:12:01 -08:00
|
|
|
use solana_vote_program::vote_state::{
|
|
|
|
self, VoteState, VoteStateVersions, MAX_LOCKOUT_HISTORY,
|
|
|
|
};
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-06-14 11:38:37 -07:00
|
|
|
// set up some dummies for a staked node (( vote ) ( stake ))
|
|
|
|
pub fn create_staked_node_accounts(stake: u64) -> ((Pubkey, Account), (Pubkey, Account)) {
|
2019-05-23 23:20:04 -07:00
|
|
|
let vote_pubkey = Pubkey::new_rand();
|
|
|
|
let vote_account = vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 1);
|
2019-05-16 08:23:31 -07:00
|
|
|
(
|
2019-05-23 23:20:04 -07:00
|
|
|
(vote_pubkey, vote_account),
|
|
|
|
create_stake_account(stake, &vote_pubkey),
|
2019-05-16 08:23:31 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
// add stake to a vote_pubkey ( stake )
|
2019-06-14 11:38:37 -07:00
|
|
|
pub fn create_stake_account(stake: u64, vote_pubkey: &Pubkey) -> (Pubkey, Account) {
|
2019-09-26 13:29:29 -07:00
|
|
|
let stake_pubkey = Pubkey::new_rand();
|
2019-05-16 08:23:31 -07:00
|
|
|
(
|
2019-09-26 13:29:29 -07:00
|
|
|
stake_pubkey,
|
2019-08-15 18:58:46 -07:00
|
|
|
stake_state::create_account(
|
2019-09-26 13:29:29 -07:00
|
|
|
&stake_pubkey,
|
2019-08-15 18:58:46 -07:00
|
|
|
&vote_pubkey,
|
|
|
|
&vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 1),
|
2019-11-12 12:33:40 -08:00
|
|
|
&Rent::free(),
|
2019-08-15 18:58:46 -07:00
|
|
|
stake,
|
|
|
|
),
|
2019-05-16 08:23:31 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_stakes_basic() {
|
2019-08-12 20:59:57 -07:00
|
|
|
for i in 0..4 {
|
2019-06-19 11:54:52 -07:00
|
|
|
let mut stakes = Stakes::default();
|
|
|
|
stakes.epoch = i;
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-06-19 11:54:52 -07:00
|
|
|
let ((vote_pubkey, vote_account), (stake_pubkey, mut stake_account)) =
|
|
|
|
create_staked_node_accounts(10);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-06-19 11:54:52 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
|
|
|
let stake = StakeState::stake_from(&stake_account).unwrap();
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
2019-08-12 20:59:57 -07:00
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
|
|
|
stake.stake(i, None)
|
|
|
|
);
|
2019-06-19 11:54:52 -07:00
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-06-19 11:54:52 -07:00
|
|
|
stake_account.lamports = 42;
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
2019-08-12 20:59:57 -07:00
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
|
|
|
stake.stake(i, None)
|
|
|
|
); // stays old stake, because only 10 is activated
|
2019-06-19 11:54:52 -07:00
|
|
|
}
|
2019-06-10 16:17:29 -07:00
|
|
|
|
2019-06-19 11:54:52 -07:00
|
|
|
// activate more
|
|
|
|
let (_stake_pubkey, mut stake_account) = create_stake_account(42, &vote_pubkey);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
|
|
|
let stake = StakeState::stake_from(&stake_account).unwrap();
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
2019-08-12 20:59:57 -07:00
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
|
|
|
stake.stake(i, None)
|
|
|
|
); // now stake of 42 is activated
|
2019-06-19 11:54:52 -07:00
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-06-19 11:54:52 -07:00
|
|
|
stake_account.lamports = 0;
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey).unwrap().0, 0);
|
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-11 11:44:58 -07:00
|
|
|
#[test]
|
|
|
|
fn test_stakes_highest() {
|
|
|
|
let mut stakes = Stakes::default();
|
|
|
|
|
|
|
|
assert_eq!(stakes.highest_staked_node(), None);
|
|
|
|
|
|
|
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
|
|
|
create_staked_node_accounts(10);
|
|
|
|
|
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
|
|
|
|
|
|
|
let ((vote11_pubkey, vote11_account), (stake11_pubkey, stake11_account)) =
|
2019-06-19 11:54:52 -07:00
|
|
|
create_staked_node_accounts(20);
|
2019-06-11 11:44:58 -07:00
|
|
|
|
|
|
|
stakes.store(&vote11_pubkey, &vote11_account);
|
|
|
|
stakes.store(&stake11_pubkey, &stake11_account);
|
|
|
|
|
|
|
|
let vote11_node_pubkey = VoteState::from(&vote11_account).unwrap().node_pubkey;
|
|
|
|
|
|
|
|
assert_eq!(stakes.highest_staked_node(), Some(vote11_node_pubkey))
|
|
|
|
}
|
|
|
|
|
2019-06-14 11:38:37 -07:00
|
|
|
#[test]
|
|
|
|
fn test_stakes_points() {
|
|
|
|
let mut stakes = Stakes::default();
|
2019-08-12 20:59:57 -07:00
|
|
|
stakes.epoch = 4;
|
2019-06-19 11:54:52 -07:00
|
|
|
|
2019-06-14 11:38:37 -07:00
|
|
|
let stake = 42;
|
|
|
|
assert_eq!(stakes.points(), 0);
|
|
|
|
assert_eq!(stakes.claim_points(), 0);
|
|
|
|
assert_eq!(stakes.claim_points(), 0);
|
|
|
|
|
|
|
|
let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
|
|
|
|
create_staked_node_accounts(stake);
|
|
|
|
|
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
|
|
|
|
|
|
|
assert_eq!(stakes.points(), 0);
|
|
|
|
assert_eq!(stakes.claim_points(), 0);
|
|
|
|
|
2020-02-25 17:12:01 -08:00
|
|
|
let mut vote_state = Some(VoteState::from(&vote_account).unwrap());
|
2019-06-14 11:38:37 -07:00
|
|
|
for i in 0..MAX_LOCKOUT_HISTORY + 42 {
|
2020-05-15 17:35:43 +01:00
|
|
|
if let Some(v) = vote_state.as_mut() {
|
|
|
|
v.process_slot_vote_unchecked(i as u64)
|
|
|
|
}
|
2020-02-25 17:12:01 -08:00
|
|
|
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"),
|
|
|
|
};
|
2019-06-14 11:38:37 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
2020-02-25 17:12:01 -08:00
|
|
|
assert_eq!(
|
|
|
|
stakes.points(),
|
|
|
|
vote_state.as_ref().unwrap().credits() * stake
|
|
|
|
);
|
2019-06-14 11:38:37 -07:00
|
|
|
}
|
|
|
|
vote_account.lamports = 0;
|
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
2020-02-25 17:12:01 -08:00
|
|
|
assert_eq!(
|
|
|
|
stakes.points(),
|
|
|
|
vote_state.as_ref().unwrap().credits() * stake
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
stakes.claim_points(),
|
|
|
|
vote_state.as_ref().unwrap().credits() * stake
|
|
|
|
);
|
2019-06-14 11:38:37 -07:00
|
|
|
assert_eq!(stakes.claim_points(), 0);
|
|
|
|
assert_eq!(stakes.claim_points(), 0);
|
|
|
|
|
|
|
|
// points come out of nowhere, but don't care here ;)
|
|
|
|
vote_account.lamports = 1;
|
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
2020-02-25 17:12:01 -08:00
|
|
|
assert_eq!(
|
|
|
|
stakes.points(),
|
|
|
|
vote_state.as_ref().unwrap().credits() * stake
|
|
|
|
);
|
2019-06-14 11:38:37 -07:00
|
|
|
|
|
|
|
// test going backwards, should never go backwards
|
|
|
|
let old_vote_state = vote_state;
|
|
|
|
let vote_account = vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 1);
|
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
2020-02-25 17:12:01 -08:00
|
|
|
assert_eq!(
|
|
|
|
stakes.points(),
|
|
|
|
old_vote_state.as_ref().unwrap().credits() * stake
|
|
|
|
);
|
2019-06-14 11:38:37 -07:00
|
|
|
}
|
|
|
|
|
2019-05-16 08:23:31 -07:00
|
|
|
#[test]
|
|
|
|
fn test_stakes_vote_account_disappear_reappear() {
|
|
|
|
let mut stakes = Stakes::default();
|
2019-08-12 20:59:57 -07:00
|
|
|
stakes.epoch = 4;
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
let ((vote_pubkey, mut vote_account), (stake_pubkey, stake_account)) =
|
2019-05-16 08:23:31 -07:00
|
|
|
create_staked_node_accounts(10);
|
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey).unwrap().0, 10);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
vote_account.lamports = 0;
|
2019-05-23 23:20:04 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_none());
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
vote_account.lamports = 1;
|
2019-05-23 23:20:04 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey).unwrap().0, 10);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_stakes_change_delegate() {
|
|
|
|
let mut stakes = Stakes::default();
|
2019-08-12 20:59:57 -07:00
|
|
|
stakes.epoch = 4;
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
|
|
|
create_staked_node_accounts(10);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
let ((vote_pubkey2, vote_account2), (_stake_pubkey2, stake_account2)) =
|
2019-05-16 08:23:31 -07:00
|
|
|
create_staked_node_accounts(10);
|
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
|
|
|
stakes.store(&vote_pubkey2, &vote_account2);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
// delegates to vote_pubkey
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-06-19 11:54:52 -07:00
|
|
|
let stake = StakeState::stake_from(&stake_account).unwrap();
|
|
|
|
|
2019-05-16 08:23:31 -07:00
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
2019-06-19 11:54:52 -07:00
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
2019-08-12 20:59:57 -07:00
|
|
|
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
2019-06-19 11:54:52 -07:00
|
|
|
);
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey2).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey2).unwrap().0, 0);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
// delegates to vote_pubkey2
|
|
|
|
stakes.store(&stake_pubkey, &stake_account2);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey).unwrap().0, 0);
|
|
|
|
assert!(vote_accounts.get(&vote_pubkey2).is_some());
|
2019-06-19 11:54:52 -07:00
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey2).unwrap().0,
|
2019-08-12 20:59:57 -07:00
|
|
|
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
2019-06-19 11:54:52 -07:00
|
|
|
);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_stakes_multiple_stakers() {
|
|
|
|
let mut stakes = Stakes::default();
|
2019-08-12 20:59:57 -07:00
|
|
|
stakes.epoch = 4;
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
|
|
|
create_staked_node_accounts(10);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
let (stake_pubkey2, stake_account2) = create_stake_account(10, &vote_pubkey);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
// delegates to vote_pubkey
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
|
|
|
stakes.store(&stake_pubkey2, &stake_account2);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey).unwrap().0, 20);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
}
|
2019-06-19 11:54:52 -07:00
|
|
|
#[test]
|
|
|
|
fn test_clone_with_epoch() {
|
|
|
|
let mut stakes = Stakes::default();
|
|
|
|
|
|
|
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
|
|
|
create_staked_node_accounts(10);
|
|
|
|
|
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
|
|
|
let stake = StakeState::stake_from(&stake_account).unwrap();
|
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
2019-08-12 20:59:57 -07:00
|
|
|
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
2019-06-19 11:54:52 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
let stakes = stakes.clone_with_epoch(3);
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
2019-08-12 20:59:57 -07:00
|
|
|
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
2019-06-19 11:54:52 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_stakes_not_delegate() {
|
|
|
|
let mut stakes = Stakes::default();
|
2019-08-12 20:59:57 -07:00
|
|
|
stakes.epoch = 4;
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
|
|
|
create_staked_node_accounts(10);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey).unwrap().0, 10);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// not a stake account, and whacks above entry
|
2019-11-20 10:12:43 -08:00
|
|
|
stakes.store(
|
|
|
|
&stake_pubkey,
|
|
|
|
&Account::new(1, 0, &solana_stake_program::id()),
|
|
|
|
);
|
2019-05-16 08:23:31 -07:00
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
2019-05-23 23:20:04 -07:00
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey).unwrap().0, 0);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|