2019-05-16 08:23:31 -07:00
|
|
|
//! Stakes serve as a cache of stake and vote accounts to derive
|
|
|
|
//! node stakes
|
2021-08-10 05:59:12 -07:00
|
|
|
use {
|
2021-08-10 07:39:50 -07:00
|
|
|
crate::vote_account::{VoteAccount, VoteAccounts},
|
2021-08-10 05:59:12 -07:00
|
|
|
solana_sdk::{
|
|
|
|
account::{AccountSharedData, ReadableAccount},
|
|
|
|
clock::Epoch,
|
|
|
|
pubkey::Pubkey,
|
|
|
|
stake::{
|
|
|
|
self,
|
|
|
|
state::{Delegation, StakeState},
|
|
|
|
},
|
|
|
|
sysvar::stake_history::StakeHistory,
|
2021-06-15 09:04:00 -07:00
|
|
|
},
|
2021-08-10 05:59:12 -07:00
|
|
|
solana_stake_program::stake_state,
|
|
|
|
solana_vote_program::vote_state::VoteState,
|
2021-08-16 11:21:33 -07:00
|
|
|
std::{collections::HashMap, sync::Arc},
|
2020-01-28 17:03:20 -08:00
|
|
|
};
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2020-07-06 04:22:23 -07:00
|
|
|
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize, AbiExample)]
|
2019-05-16 08:23:31 -07:00
|
|
|
pub struct Stakes {
|
|
|
|
/// vote accounts
|
2020-12-17 13:22:50 -08:00
|
|
|
vote_accounts: VoteAccounts,
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-11-25 13:14:32 -08:00
|
|
|
/// stake_delegations
|
|
|
|
stake_delegations: HashMap<Pubkey, Delegation>,
|
2019-06-14 11:38:37 -07:00
|
|
|
|
2020-07-20 21:57:25 -07:00
|
|
|
/// unused
|
|
|
|
unused: 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
|
|
|
|
}
|
2021-08-19 22:08:44 -07:00
|
|
|
pub fn clone_with_epoch(&self, next_epoch: Epoch) -> Self {
|
2020-11-04 03:18:05 -08:00
|
|
|
let prev_epoch = self.epoch;
|
|
|
|
if prev_epoch == next_epoch {
|
2019-06-19 11:54:52 -07:00
|
|
|
self.clone()
|
|
|
|
} else {
|
2020-11-04 03:18:05 -08:00
|
|
|
// wrap up the prev epoch by adding new stake history entry for the prev epoch
|
|
|
|
let mut stake_history_upto_prev_epoch = self.stake_history.clone();
|
|
|
|
stake_history_upto_prev_epoch.add(
|
|
|
|
prev_epoch,
|
2021-06-15 09:04:00 -07:00
|
|
|
stake_state::new_stake_history_entry(
|
2020-11-04 03:18:05 -08:00
|
|
|
prev_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),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2020-11-04 03:18:05 -08:00
|
|
|
// refresh the stake distribution of vote accounts for the next epoch, using new stake history
|
|
|
|
let vote_accounts_for_next_epoch = self
|
|
|
|
.vote_accounts
|
|
|
|
.iter()
|
2020-12-17 13:22:50 -08:00
|
|
|
.map(|(pubkey, (_ /*stake*/, account))| {
|
|
|
|
let stake = self.calculate_stake(
|
|
|
|
pubkey,
|
|
|
|
next_epoch,
|
|
|
|
Some(&stake_history_upto_prev_epoch),
|
|
|
|
);
|
|
|
|
(*pubkey, (stake, account.clone()))
|
2020-11-04 03:18:05 -08:00
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2019-06-19 11:54:52 -07:00
|
|
|
Stakes {
|
2019-11-25 13:14:32 -08:00
|
|
|
stake_delegations: self.stake_delegations.clone(),
|
2020-07-20 21:57:25 -07:00
|
|
|
unused: self.unused,
|
2020-11-04 03:18:05 -08:00
|
|
|
epoch: next_epoch,
|
|
|
|
stake_history: stake_history_upto_prev_epoch,
|
|
|
|
vote_accounts: vote_accounts_for_next_epoch,
|
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 {
|
2021-08-19 22:08:44 -07:00
|
|
|
stake_delegation.stake(epoch, stake_history)
|
2019-11-25 13:14:32 -08:00
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
})
|
|
|
|
.sum()
|
|
|
|
}
|
|
|
|
|
2020-07-20 21:57:25 -07:00
|
|
|
pub fn vote_balance_and_staked(&self) -> u64 {
|
|
|
|
self.stake_delegations
|
|
|
|
.iter()
|
|
|
|
.map(|(_, stake_delegation)| stake_delegation.stake)
|
|
|
|
.sum::<u64>()
|
|
|
|
+ self
|
|
|
|
.vote_accounts
|
|
|
|
.iter()
|
2020-11-30 09:18:33 -08:00
|
|
|
.map(|(_pubkey, (_staked, vote_account))| vote_account.lamports())
|
2020-07-20 21:57:25 -07:00
|
|
|
.sum::<u64>()
|
|
|
|
}
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
pub fn is_stake(account: &AccountSharedData) -> bool {
|
2021-04-26 10:06:40 -07:00
|
|
|
solana_vote_program::check_id(account.owner())
|
2021-06-15 09:04:00 -07:00
|
|
|
|| stake::program::check_id(account.owner())
|
2021-03-09 14:31:33 -08:00
|
|
|
&& account.data().len() >= std::mem::size_of::<StakeState>()
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
|
2020-11-11 13:11:57 -08:00
|
|
|
pub fn store(
|
|
|
|
&mut self,
|
|
|
|
pubkey: &Pubkey,
|
2021-03-09 13:06:07 -08:00
|
|
|
account: &AccountSharedData,
|
2021-02-24 09:00:48 -08:00
|
|
|
check_vote_init: bool,
|
2021-08-10 07:39:50 -07:00
|
|
|
) -> Option<VoteAccount> {
|
2021-04-26 10:06:40 -07:00
|
|
|
if solana_vote_program::check_id(account.owner()) {
|
2020-12-11 01:13:36 -08:00
|
|
|
// unconditionally remove existing at first; there is no dependent calculated state for
|
|
|
|
// votes, not like stakes (stake codepath maintains calculated stake value grouped by
|
|
|
|
// delegated vote pubkey)
|
2020-08-07 11:21:35 -07:00
|
|
|
let old = self.vote_accounts.remove(pubkey);
|
2021-02-24 09:00:48 -08:00
|
|
|
// when account is removed (lamports == 0 or data uninitialized), don't read so that
|
|
|
|
// given `pubkey` can be used for any owner in the future, while not affecting Stakes.
|
2021-04-22 13:04:55 -07:00
|
|
|
if account.lamports() != 0
|
2021-06-18 06:34:46 -07:00
|
|
|
&& !(check_vote_init && VoteState::is_uninitialized_no_deser(account.data()))
|
2021-02-24 09:00:48 -08:00
|
|
|
{
|
2020-08-07 11:21:35 -07:00
|
|
|
let stake = old.as_ref().map_or_else(
|
2021-08-19 22:08:44 -07:00
|
|
|
|| self.calculate_stake(pubkey, self.epoch, Some(&self.stake_history)),
|
2019-08-12 20:59:57 -07:00
|
|
|
|v| v.0,
|
|
|
|
);
|
2019-06-14 11:38:37 -07:00
|
|
|
|
2020-11-30 09:18:33 -08:00
|
|
|
self.vote_accounts
|
2021-08-10 07:39:50 -07:00
|
|
|
.insert(*pubkey, (stake, VoteAccount::from(account.clone())));
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
2020-08-07 11:21:35 -07:00
|
|
|
old.map(|(_, account)| account)
|
2021-06-15 09:04:00 -07:00
|
|
|
} else if 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,
|
2021-08-19 22:08:44 -07:00
|
|
|
delegation.stake(self.epoch, Some(&self.stake_history)),
|
2019-11-25 13:14:32 -08:00
|
|
|
)
|
2019-06-19 11:54:52 -07:00
|
|
|
});
|
|
|
|
|
2021-06-15 09:04:00 -07:00
|
|
|
let delegation = stake_state::delegation_from(account);
|
2019-11-25 13:14:32 -08:00
|
|
|
|
|
|
|
let stake = delegation.map(|delegation| {
|
2019-06-19 11:54:52 -07:00
|
|
|
(
|
2019-11-25 13:14:32 -08:00
|
|
|
delegation.voter_pubkey,
|
2021-04-22 13:04:55 -07:00
|
|
|
if account.lamports() != 0 {
|
2021-08-19 22:08:44 -07:00
|
|
|
delegation.stake(self.epoch, Some(&self.stake_history))
|
2019-06-19 11:54:52 -07:00
|
|
|
} else {
|
2020-12-11 01:13:36 -08:00
|
|
|
// when account is removed (lamports == 0), this special `else` clause ensures
|
|
|
|
// resetting cached stake value below, even if the account happens to be
|
|
|
|
// still staked for some (odd) reason
|
2019-06-19 11:54:52 -07:00
|
|
|
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 {
|
2020-12-17 13:22:50 -08:00
|
|
|
self.vote_accounts.sub_stake(&voter_pubkey, stake);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
2019-06-10 16:17:29 -07:00
|
|
|
if let Some((voter_pubkey, stake)) = stake {
|
2020-12-17 13:22:50 -08:00
|
|
|
self.vote_accounts.add_stake(&voter_pubkey, stake);
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 08:44:46 -07:00
|
|
|
if account.lamports() == 0 {
|
2020-12-11 01:13:36 -08:00
|
|
|
// when account is removed (lamports == 0), remove it from Stakes as well
|
|
|
|
// so that given `pubkey` can be used for any owner in the future, while not
|
|
|
|
// affecting Stakes.
|
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
|
|
|
}
|
2020-08-07 11:21:35 -07:00
|
|
|
None
|
|
|
|
} else {
|
2020-12-11 01:13:36 -08:00
|
|
|
// there is no need to remove possibly existing Stakes cache entries with given
|
|
|
|
// `pubkey` because this isn't possible, first of all.
|
|
|
|
// Runtime always enforces an intermediary write of account.lamports == 0,
|
|
|
|
// when not-System111-owned account.owner is swapped.
|
2020-08-07 11:21:35 -07:00
|
|
|
None
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
|
|
|
}
|
2019-09-30 18:57:49 -07:00
|
|
|
|
2021-08-30 08:54:01 -07:00
|
|
|
pub fn vote_accounts(&self) -> &VoteAccounts {
|
|
|
|
&self.vote_accounts
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|
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
|
|
|
|
}
|
|
|
|
|
2021-08-10 05:59:12 -07:00
|
|
|
pub fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
|
2020-12-17 13:22:50 -08:00
|
|
|
self.vote_accounts.staked_nodes()
|
|
|
|
}
|
|
|
|
|
2019-06-11 11:44:58 -07:00
|
|
|
pub fn highest_staked_node(&self) -> Option<Pubkey> {
|
2020-11-30 09:18:33 -08:00
|
|
|
let (_pubkey, (_stake, vote_account)) = self
|
|
|
|
.vote_accounts
|
2019-06-11 11:44:58 -07:00
|
|
|
.iter()
|
2020-11-30 09:18:33 -08:00
|
|
|
.max_by(|(_ak, av), (_bk, bv)| av.0.cmp(&bv.0))?;
|
|
|
|
let node_pubkey = vote_account.vote_state().as_ref().ok()?.node_pubkey;
|
|
|
|
Some(node_pubkey)
|
2019-06-11 11:44:58 -07:00
|
|
|
}
|
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::*;
|
2021-04-22 11:56:47 -07:00
|
|
|
use solana_sdk::{account::WritableAccount, pubkey::Pubkey, rent::Rent};
|
2019-11-20 10:12:43 -08:00
|
|
|
use solana_stake_program::stake_state;
|
2021-02-24 09:00:48 -08:00
|
|
|
use solana_vote_program::vote_state::{self, VoteState, VoteStateVersions};
|
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 ))
|
2021-03-09 13:06:07 -08:00
|
|
|
pub fn create_staked_node_accounts(
|
|
|
|
stake: u64,
|
|
|
|
) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
|
2020-10-19 12:12:08 -07:00
|
|
|
let vote_pubkey = solana_sdk::pubkey::new_rand();
|
2020-10-19 12:23:14 -07:00
|
|
|
let vote_account =
|
|
|
|
vote_state::create_account(&vote_pubkey, &solana_sdk::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 )
|
2021-03-09 13:06:07 -08:00
|
|
|
pub fn create_stake_account(stake: u64, vote_pubkey: &Pubkey) -> (Pubkey, AccountSharedData) {
|
2020-10-19 12:12:08 -07:00
|
|
|
let stake_pubkey = solana_sdk::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,
|
2021-06-18 06:34:46 -07:00
|
|
|
vote_pubkey,
|
|
|
|
&vote_state::create_account(vote_pubkey, &solana_sdk::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
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-07-20 21:57:25 -07:00
|
|
|
pub fn create_warming_staked_node_accounts(
|
|
|
|
stake: u64,
|
|
|
|
epoch: Epoch,
|
2021-03-09 13:06:07 -08:00
|
|
|
) -> ((Pubkey, AccountSharedData), (Pubkey, AccountSharedData)) {
|
2020-10-19 12:12:08 -07:00
|
|
|
let vote_pubkey = solana_sdk::pubkey::new_rand();
|
2020-10-19 12:23:14 -07:00
|
|
|
let vote_account =
|
|
|
|
vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1);
|
2020-07-20 21:57:25 -07:00
|
|
|
(
|
|
|
|
(vote_pubkey, vote_account),
|
|
|
|
create_warming_stake_account(stake, epoch, &vote_pubkey),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-12-13 17:26:34 -08:00
|
|
|
// add stake to a vote_pubkey ( stake )
|
2020-07-20 21:57:25 -07:00
|
|
|
pub fn create_warming_stake_account(
|
|
|
|
stake: u64,
|
|
|
|
epoch: Epoch,
|
|
|
|
vote_pubkey: &Pubkey,
|
2021-03-09 13:06:07 -08:00
|
|
|
) -> (Pubkey, AccountSharedData) {
|
2020-10-19 12:12:08 -07:00
|
|
|
let stake_pubkey = solana_sdk::pubkey::new_rand();
|
2020-07-20 21:57:25 -07:00
|
|
|
(
|
|
|
|
stake_pubkey,
|
|
|
|
stake_state::create_account_with_activation_epoch(
|
|
|
|
&stake_pubkey,
|
2021-06-18 06:34:46 -07:00
|
|
|
vote_pubkey,
|
|
|
|
&vote_state::create_account(vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, 1),
|
2020-07-20 21:57:25 -07:00
|
|
|
&Rent::free(),
|
|
|
|
stake,
|
|
|
|
epoch,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
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 {
|
2020-12-13 17:26:34 -08:00
|
|
|
let mut stakes = Stakes {
|
|
|
|
epoch: i,
|
|
|
|
..Stakes::default()
|
|
|
|
};
|
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
|
|
|
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
2021-06-15 09:04:00 -07:00
|
|
|
let stake = stake_state::stake_from(&stake_account).unwrap();
|
2019-06-19 11:54:52 -07:00
|
|
|
{
|
|
|
|
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,
|
2021-08-19 22:08:44 -07:00
|
|
|
stake.stake(i, None)
|
2019-08-12 20:59:57 -07:00
|
|
|
);
|
2019-06-19 11:54:52 -07:00
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2021-04-22 11:56:47 -07:00
|
|
|
stake_account.set_lamports(42);
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
2019-06-19 11:54:52 -07:00
|
|
|
{
|
|
|
|
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,
|
2021-08-19 22:08:44 -07:00
|
|
|
stake.stake(i, None)
|
2019-08-12 20:59:57 -07:00
|
|
|
); // 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);
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
2021-06-15 09:04:00 -07:00
|
|
|
let stake = stake_state::stake_from(&stake_account).unwrap();
|
2019-06-19 11:54:52 -07:00
|
|
|
{
|
|
|
|
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,
|
2021-08-19 22:08:44 -07:00
|
|
|
stake.stake(i, None)
|
2019-08-12 20:59:57 -07:00
|
|
|
); // now stake of 42 is activated
|
2019-06-19 11:54:52 -07:00
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2021-04-22 11:56:47 -07:00
|
|
|
stake_account.set_lamports(0);
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
2019-06-19 11:54:52 -07:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
2019-06-11 11:44:58 -07:00
|
|
|
|
|
|
|
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
|
|
|
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote11_pubkey, &vote11_account, true);
|
|
|
|
stakes.store(&stake11_pubkey, &stake11_account, true);
|
2019-06-11 11:44:58 -07:00
|
|
|
|
|
|
|
let vote11_node_pubkey = VoteState::from(&vote11_account).unwrap().node_pubkey;
|
|
|
|
|
|
|
|
assert_eq!(stakes.highest_staked_node(), Some(vote11_node_pubkey))
|
|
|
|
}
|
|
|
|
|
2019-05-16 08:23:31 -07:00
|
|
|
#[test]
|
|
|
|
fn test_stakes_vote_account_disappear_reappear() {
|
2020-12-13 17:26:34 -08:00
|
|
|
let mut stakes = Stakes {
|
|
|
|
epoch: 4,
|
|
|
|
..Stakes::default()
|
|
|
|
};
|
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);
|
|
|
|
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
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
|
|
|
}
|
|
|
|
|
2021-04-22 11:56:47 -07:00
|
|
|
vote_account.set_lamports(0);
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
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
|
|
|
}
|
2021-02-24 09:00:48 -08:00
|
|
|
|
2021-04-22 11:56:47 -07:00
|
|
|
vote_account.set_lamports(1);
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
2021-02-24 09:00:48 -08:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_some());
|
|
|
|
assert_eq!(vote_accounts.get(&vote_pubkey).unwrap().0, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Vote account too big
|
2021-03-09 14:31:33 -08:00
|
|
|
let cache_data = vote_account.data().to_vec();
|
2021-03-25 09:04:20 -07:00
|
|
|
let mut pushed = vote_account.data().to_vec();
|
2021-03-23 13:19:31 -07:00
|
|
|
pushed.push(0);
|
|
|
|
vote_account.set_data(pushed);
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
2021-02-24 09:00:48 -08:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_none());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Vote account uninitialized
|
|
|
|
let default_vote_state = VoteState::default();
|
|
|
|
let versioned = VoteStateVersions::new_current(default_vote_state);
|
|
|
|
VoteState::to(&versioned, &mut vote_account).unwrap();
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
2021-02-24 09:00:48 -08:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert!(vote_accounts.get(&vote_pubkey).is_none());
|
|
|
|
}
|
|
|
|
|
2021-03-11 14:40:45 -08:00
|
|
|
vote_account.set_data(cache_data);
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
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() {
|
2020-12-13 17:26:34 -08:00
|
|
|
let mut stakes = Stakes {
|
|
|
|
epoch: 4,
|
|
|
|
..Stakes::default()
|
|
|
|
};
|
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);
|
|
|
|
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
|
|
|
stakes.store(&vote_pubkey2, &vote_account2, true);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
// delegates to vote_pubkey
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2021-06-15 09:04:00 -07:00
|
|
|
let stake = stake_state::stake_from(&stake_account).unwrap();
|
2019-06-19 11:54:52 -07:00
|
|
|
|
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,
|
2021-08-19 22:08:44 -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
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&stake_pubkey, &stake_account2, true);
|
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,
|
2021-08-19 22:08:44 -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() {
|
2020-12-13 17:26:34 -08:00
|
|
|
let mut stakes = Stakes {
|
|
|
|
epoch: 4,
|
|
|
|
..Stakes::default()
|
|
|
|
};
|
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
|
|
|
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
2019-05-16 08:23:31 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
// delegates to vote_pubkey
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
|
|
|
stakes.store(&stake_pubkey2, &stake_account2, true);
|
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);
|
|
|
|
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
2021-06-15 09:04:00 -07:00
|
|
|
let stake = stake_state::stake_from(&stake_account).unwrap();
|
2019-06-19 11:54:52 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
2021-08-19 22:08:44 -07:00
|
|
|
stake.stake(stakes.epoch, Some(&stakes.stake_history))
|
2019-06-19 11:54:52 -07:00
|
|
|
);
|
|
|
|
}
|
2021-08-19 22:08:44 -07:00
|
|
|
let stakes = stakes.clone_with_epoch(3);
|
2019-06-19 11:54:52 -07:00
|
|
|
{
|
|
|
|
let vote_accounts = stakes.vote_accounts();
|
|
|
|
assert_eq!(
|
|
|
|
vote_accounts.get(&vote_pubkey).unwrap().0,
|
2021-08-19 22:08:44 -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() {
|
2020-12-13 17:26:34 -08:00
|
|
|
let mut stakes = Stakes {
|
|
|
|
epoch: 4,
|
|
|
|
..Stakes::default()
|
|
|
|
};
|
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
|
|
|
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
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,
|
2021-06-15 09:04:00 -07:00
|
|
|
&AccountSharedData::new(1, 0, &stake::program::id()),
|
2020-11-11 13:11:57 -08:00
|
|
|
true,
|
2019-11-20 10:12:43 -08:00
|
|
|
);
|
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
|
|
|
}
|
|
|
|
}
|
2020-07-20 21:57:25 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_vote_balance_and_staked_empty() {
|
|
|
|
let stakes = Stakes::default();
|
|
|
|
assert_eq!(stakes.vote_balance_and_staked(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_vote_balance_and_staked_normal() {
|
|
|
|
let mut stakes = Stakes::default();
|
|
|
|
impl Stakes {
|
|
|
|
pub fn vote_balance_and_warmed_staked(&self) -> u64 {
|
|
|
|
self.vote_accounts
|
|
|
|
.iter()
|
2020-11-30 09:18:33 -08:00
|
|
|
.map(|(_pubkey, (staked, account))| staked + account.lamports())
|
2020-07-20 21:57:25 -07:00
|
|
|
.sum()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let genesis_epoch = 0;
|
|
|
|
let ((vote_pubkey, vote_account), (stake_pubkey, stake_account)) =
|
|
|
|
create_warming_staked_node_accounts(10, genesis_epoch);
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes.store(&vote_pubkey, &vote_account, true);
|
|
|
|
stakes.store(&stake_pubkey, &stake_account, true);
|
2020-07-20 21:57:25 -07:00
|
|
|
|
|
|
|
assert_eq!(stakes.vote_balance_and_staked(), 11);
|
|
|
|
assert_eq!(stakes.vote_balance_and_warmed_staked(), 1);
|
|
|
|
|
|
|
|
for (epoch, expected_warmed_stake) in ((genesis_epoch + 1)..=3).zip(&[2, 3, 4]) {
|
2021-08-19 22:08:44 -07:00
|
|
|
stakes = stakes.clone_with_epoch(epoch);
|
2020-07-20 21:57:25 -07:00
|
|
|
// vote_balance_and_staked() always remain to return same lamports
|
|
|
|
// while vote_balance_and_warmed_staked() gradually increases
|
|
|
|
assert_eq!(stakes.vote_balance_and_staked(), 11);
|
|
|
|
assert_eq!(
|
|
|
|
stakes.vote_balance_and_warmed_staked(),
|
|
|
|
*expected_warmed_stake
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-05-16 08:23:31 -07:00
|
|
|
}
|