solana_accounts_db/
stake_rewards.rs

1//! Code for stake and vote rewards
2
3use {
4    crate::{
5        is_zero_lamport::IsZeroLamport,
6        storable_accounts::{AccountForStorage, StorableAccounts},
7    },
8    solana_account::{AccountSharedData, ReadableAccount},
9    solana_clock::Slot,
10    solana_pubkey::Pubkey,
11    solana_reward_info::RewardInfo,
12};
13
14#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
15#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
16pub struct StakeReward {
17    pub stake_pubkey: Pubkey,
18    pub stake_reward_info: RewardInfo,
19    pub stake_account: AccountSharedData,
20}
21
22impl StakeReward {
23    pub fn get_stake_reward(&self) -> i64 {
24        self.stake_reward_info.lamports
25    }
26}
27
28impl IsZeroLamport for StakeReward {
29    fn is_zero_lamport(&self) -> bool {
30        self.stake_account.lamports() == 0
31    }
32}
33
34/// allow [StakeReward] to be passed to `StoreAccounts` directly without copies or vec construction
35impl<'a> StorableAccounts<'a> for (Slot, &'a [StakeReward]) {
36    fn account<Ret>(
37        &self,
38        index: usize,
39        mut callback: impl for<'local> FnMut(AccountForStorage<'local>) -> Ret,
40    ) -> Ret {
41        let entry = &self.1[index];
42        callback((&self.1[index].stake_pubkey, &entry.stake_account).into())
43    }
44    fn is_zero_lamport(&self, index: usize) -> bool {
45        self.1[index].is_zero_lamport()
46    }
47    fn data_len(&self, index: usize) -> usize {
48        self.1[index].stake_account.data().len()
49    }
50    fn pubkey(&self, index: usize) -> &Pubkey {
51        &self.1[index].stake_pubkey
52    }
53    fn slot(&self, _index: usize) -> Slot {
54        // per-index slot is not unique per slot when per-account slot is not included in the source data
55        self.target_slot()
56    }
57    fn target_slot(&self) -> Slot {
58        self.0
59    }
60    fn len(&self) -> usize {
61        self.1.len()
62    }
63}
64
65#[cfg(feature = "dev-context-only-utils")]
66use {
67    rand::Rng, solana_account::WritableAccount, solana_keypair::Keypair, solana_rent::Rent,
68    solana_signer::Signer, solana_stake_program::stake_state, solana_vote_program::vote_state,
69};
70
71// These functions/fields are only usable from a dev context (i.e. tests and benches)
72#[cfg(feature = "dev-context-only-utils")]
73impl StakeReward {
74    pub fn new_random() -> Self {
75        let mut rng = rand::thread_rng();
76
77        let rent = Rent::free();
78
79        let validator_pubkey = solana_pubkey::new_rand();
80        let validator_stake_lamports = 20;
81        let validator_staking_keypair = Keypair::new();
82        let validator_voting_keypair = Keypair::new();
83
84        let validator_vote_account = vote_state::create_account(
85            &validator_voting_keypair.pubkey(),
86            &validator_pubkey,
87            10,
88            validator_stake_lamports,
89        );
90
91        let reward_lamports: i64 = rng.gen_range(1..200);
92        let validator_stake_account = stake_state::create_account(
93            &validator_staking_keypair.pubkey(),
94            &validator_voting_keypair.pubkey(),
95            &validator_vote_account,
96            &rent,
97            validator_stake_lamports + reward_lamports as u64,
98        );
99
100        Self {
101            stake_pubkey: Pubkey::new_unique(),
102            stake_reward_info: RewardInfo {
103                reward_type: solana_reward_info::RewardType::Staking,
104                lamports: reward_lamports,
105                post_balance: 0,     /* unused atm */
106                commission: Some(0), /* unused but tests require some value */
107            },
108
109            stake_account: validator_stake_account,
110        }
111    }
112
113    pub fn credit(&mut self, amount: u64) {
114        self.stake_reward_info.lamports = amount as i64;
115        self.stake_reward_info.post_balance += amount;
116        self.stake_account.checked_add_lamports(amount).unwrap();
117    }
118}