solana_runtime/
epoch_stakes.rs

1use {
2    crate::stakes::{serde_stakes_to_delegation_format, SerdeStakesToStakeFormat, StakesEnum},
3    serde::{Deserialize, Serialize},
4    solana_clock::Epoch,
5    solana_pubkey::Pubkey,
6    solana_vote::vote_account::VoteAccountsHashMap,
7    std::{collections::HashMap, sync::Arc},
8};
9
10pub type NodeIdToVoteAccounts = HashMap<Pubkey, NodeVoteAccounts>;
11pub type EpochAuthorizedVoters = HashMap<Pubkey, Pubkey>;
12
13#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
14#[derive(Clone, Serialize, Debug, Deserialize, Default, PartialEq, Eq)]
15pub struct NodeVoteAccounts {
16    pub vote_accounts: Vec<Pubkey>,
17    pub total_stake: u64,
18}
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
21#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
22#[cfg_attr(feature = "dev-context-only-utils", derive(PartialEq))]
23pub struct EpochStakes {
24    #[serde(with = "serde_stakes_to_delegation_format")]
25    stakes: Arc<StakesEnum>,
26    total_stake: u64,
27    node_id_to_vote_accounts: Arc<NodeIdToVoteAccounts>,
28    epoch_authorized_voters: Arc<EpochAuthorizedVoters>,
29}
30
31impl EpochStakes {
32    pub(crate) fn new(stakes: Arc<StakesEnum>, leader_schedule_epoch: Epoch) -> Self {
33        let epoch_vote_accounts = stakes.vote_accounts();
34        let (total_stake, node_id_to_vote_accounts, epoch_authorized_voters) =
35            Self::parse_epoch_vote_accounts(epoch_vote_accounts.as_ref(), leader_schedule_epoch);
36        Self {
37            stakes,
38            total_stake,
39            node_id_to_vote_accounts: Arc::new(node_id_to_vote_accounts),
40            epoch_authorized_voters: Arc::new(epoch_authorized_voters),
41        }
42    }
43
44    #[cfg(feature = "dev-context-only-utils")]
45    pub fn new_for_tests(
46        vote_accounts_hash_map: VoteAccountsHashMap,
47        leader_schedule_epoch: Epoch,
48    ) -> Self {
49        Self::new(
50            Arc::new(StakesEnum::Accounts(crate::stakes::Stakes::new_for_tests(
51                0,
52                solana_vote::vote_account::VoteAccounts::from(Arc::new(vote_accounts_hash_map)),
53                im::HashMap::default(),
54            ))),
55            leader_schedule_epoch,
56        )
57    }
58
59    pub fn stakes(&self) -> &StakesEnum {
60        &self.stakes
61    }
62
63    pub fn total_stake(&self) -> u64 {
64        self.total_stake
65    }
66
67    /// For tests
68    pub fn set_total_stake(&mut self, total_stake: u64) {
69        self.total_stake = total_stake;
70    }
71
72    pub fn node_id_to_vote_accounts(&self) -> &Arc<NodeIdToVoteAccounts> {
73        &self.node_id_to_vote_accounts
74    }
75
76    pub fn node_id_to_stake(&self, node_id: &Pubkey) -> Option<u64> {
77        self.node_id_to_vote_accounts
78            .get(node_id)
79            .map(|x| x.total_stake)
80    }
81
82    pub fn epoch_authorized_voters(&self) -> &Arc<EpochAuthorizedVoters> {
83        &self.epoch_authorized_voters
84    }
85
86    pub fn vote_account_stake(&self, vote_account: &Pubkey) -> u64 {
87        self.stakes
88            .vote_accounts()
89            .get_delegated_stake(vote_account)
90    }
91
92    fn parse_epoch_vote_accounts(
93        epoch_vote_accounts: &VoteAccountsHashMap,
94        leader_schedule_epoch: Epoch,
95    ) -> (u64, NodeIdToVoteAccounts, EpochAuthorizedVoters) {
96        let mut node_id_to_vote_accounts: NodeIdToVoteAccounts = HashMap::new();
97        let total_stake = epoch_vote_accounts
98            .iter()
99            .map(|(_, (stake, _))| stake)
100            .sum();
101        let epoch_authorized_voters = epoch_vote_accounts
102            .iter()
103            .filter_map(|(key, (stake, account))| {
104                let vote_state = account.vote_state_view();
105
106                if *stake > 0 {
107                    if let Some(authorized_voter) =
108                        vote_state.get_authorized_voter(leader_schedule_epoch)
109                    {
110                        let node_vote_accounts = node_id_to_vote_accounts
111                            .entry(*vote_state.node_pubkey())
112                            .or_default();
113
114                        node_vote_accounts.total_stake += stake;
115                        node_vote_accounts.vote_accounts.push(*key);
116
117                        Some((*key, *authorized_voter))
118                    } else {
119                        None
120                    }
121                } else {
122                    None
123                }
124            })
125            .collect();
126        (
127            total_stake,
128            node_id_to_vote_accounts,
129            epoch_authorized_voters,
130        )
131    }
132}
133
134#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
135#[cfg_attr(feature = "dev-context-only-utils", derive(PartialEq))]
136#[derive(Debug, Clone, Serialize, Deserialize)]
137pub enum VersionedEpochStakes {
138    Current {
139        stakes: SerdeStakesToStakeFormat,
140        total_stake: u64,
141        node_id_to_vote_accounts: Arc<NodeIdToVoteAccounts>,
142        epoch_authorized_voters: Arc<EpochAuthorizedVoters>,
143    },
144}
145
146impl From<VersionedEpochStakes> for EpochStakes {
147    fn from(versioned: VersionedEpochStakes) -> Self {
148        let VersionedEpochStakes::Current {
149            stakes,
150            total_stake,
151            node_id_to_vote_accounts,
152            epoch_authorized_voters,
153        } = versioned;
154
155        Self {
156            stakes: Arc::new(stakes.into()),
157            total_stake,
158            node_id_to_vote_accounts,
159            epoch_authorized_voters,
160        }
161    }
162}
163
164/// Only the `StakesEnum::Delegations` variant is unable to be serialized as a
165/// `StakesEnum::Stakes` variant, so leave those entries and split off the other
166/// epoch stakes enum variants into a new map which will be serialized into the
167/// new `versioned_epoch_stakes` snapshot field.  After a cluster transitions to
168/// serializing epoch stakes in the new format, `StakesEnum::Delegations`
169/// variants for recent epochs will no longer be created and can be deprecated.
170pub(crate) fn split_epoch_stakes(
171    bank_epoch_stakes: HashMap<Epoch, EpochStakes>,
172) -> (
173    HashMap<Epoch, EpochStakes>,
174    HashMap<Epoch, VersionedEpochStakes>,
175) {
176    let mut old_epoch_stakes = HashMap::new();
177    let mut versioned_epoch_stakes = HashMap::new();
178    for (epoch, epoch_stakes) in bank_epoch_stakes.into_iter() {
179        let EpochStakes {
180            stakes,
181            total_stake,
182            node_id_to_vote_accounts,
183            epoch_authorized_voters,
184        } = epoch_stakes;
185        match stakes.as_ref() {
186            StakesEnum::Delegations(_) => {
187                old_epoch_stakes.insert(
188                    epoch,
189                    EpochStakes {
190                        stakes: stakes.clone(),
191                        total_stake,
192                        node_id_to_vote_accounts,
193                        epoch_authorized_voters,
194                    },
195                );
196            }
197            StakesEnum::Accounts(stakes) => {
198                versioned_epoch_stakes.insert(
199                    epoch,
200                    VersionedEpochStakes::Current {
201                        stakes: SerdeStakesToStakeFormat::Account(stakes.clone()),
202                        total_stake,
203                        node_id_to_vote_accounts,
204                        epoch_authorized_voters,
205                    },
206                );
207            }
208            StakesEnum::Stakes(stakes) => {
209                versioned_epoch_stakes.insert(
210                    epoch,
211                    VersionedEpochStakes::Current {
212                        stakes: SerdeStakesToStakeFormat::Stake(stakes.clone()),
213                        total_stake,
214                        node_id_to_vote_accounts,
215                        epoch_authorized_voters,
216                    },
217                );
218            }
219        }
220    }
221    (old_epoch_stakes, versioned_epoch_stakes)
222}
223
224#[cfg(test)]
225pub(crate) mod tests {
226    use {
227        super::*,
228        crate::{
229            stake_account::StakeAccount,
230            stakes::{Stakes, StakesCache},
231        },
232        solana_account::AccountSharedData,
233        solana_rent::Rent,
234        solana_stake_program::stake_state::{self, Delegation, Stake},
235        solana_vote::vote_account::VoteAccount,
236        solana_vote_program::vote_state::{self, create_account_with_authorized},
237        std::iter,
238    };
239
240    struct VoteAccountInfo {
241        vote_account: Pubkey,
242        account: AccountSharedData,
243        authorized_voter: Pubkey,
244    }
245
246    fn new_vote_accounts(
247        num_nodes: usize,
248        num_vote_accounts_per_node: usize,
249    ) -> HashMap<Pubkey, Vec<VoteAccountInfo>> {
250        // Create some vote accounts for each pubkey
251        (0..num_nodes)
252            .map(|_| {
253                let node_id = solana_pubkey::new_rand();
254                (
255                    node_id,
256                    iter::repeat_with(|| {
257                        let authorized_voter = solana_pubkey::new_rand();
258                        VoteAccountInfo {
259                            vote_account: solana_pubkey::new_rand(),
260                            account: create_account_with_authorized(
261                                &node_id,
262                                &authorized_voter,
263                                &node_id,
264                                0,
265                                100,
266                            ),
267                            authorized_voter,
268                        }
269                    })
270                    .take(num_vote_accounts_per_node)
271                    .collect(),
272                )
273            })
274            .collect()
275    }
276
277    fn new_epoch_vote_accounts(
278        vote_accounts_map: &HashMap<Pubkey, Vec<VoteAccountInfo>>,
279        node_id_to_stake_fn: impl Fn(&Pubkey) -> u64,
280    ) -> VoteAccountsHashMap {
281        // Create and process the vote accounts
282        vote_accounts_map
283            .iter()
284            .flat_map(|(node_id, vote_accounts)| {
285                vote_accounts.iter().map(|v| {
286                    let vote_account = VoteAccount::try_from(v.account.clone()).unwrap();
287                    (v.vote_account, (node_id_to_stake_fn(node_id), vote_account))
288                })
289            })
290            .collect()
291    }
292
293    #[test]
294    fn test_parse_epoch_vote_accounts() {
295        let stake_per_account = 100;
296        let num_vote_accounts_per_node = 2;
297        let num_nodes = 10;
298
299        let vote_accounts_map = new_vote_accounts(num_nodes, num_vote_accounts_per_node);
300
301        let expected_authorized_voters: HashMap<_, _> = vote_accounts_map
302            .iter()
303            .flat_map(|(_, vote_accounts)| {
304                vote_accounts
305                    .iter()
306                    .map(|v| (v.vote_account, v.authorized_voter))
307            })
308            .collect();
309
310        let expected_node_id_to_vote_accounts: HashMap<_, _> = vote_accounts_map
311            .iter()
312            .map(|(node_pubkey, vote_accounts)| {
313                let mut vote_accounts = vote_accounts
314                    .iter()
315                    .map(|v| (v.vote_account))
316                    .collect::<Vec<_>>();
317                vote_accounts.sort();
318                let node_vote_accounts = NodeVoteAccounts {
319                    vote_accounts,
320                    total_stake: stake_per_account * num_vote_accounts_per_node as u64,
321                };
322                (*node_pubkey, node_vote_accounts)
323            })
324            .collect();
325
326        let epoch_vote_accounts =
327            new_epoch_vote_accounts(&vote_accounts_map, |_| stake_per_account);
328
329        let (total_stake, mut node_id_to_vote_accounts, epoch_authorized_voters) =
330            EpochStakes::parse_epoch_vote_accounts(&epoch_vote_accounts, 0);
331
332        // Verify the results
333        node_id_to_vote_accounts
334            .iter_mut()
335            .for_each(|(_, node_vote_accounts)| node_vote_accounts.vote_accounts.sort());
336
337        assert!(
338            node_id_to_vote_accounts.len() == expected_node_id_to_vote_accounts.len()
339                && node_id_to_vote_accounts
340                    .iter()
341                    .all(|(k, v)| expected_node_id_to_vote_accounts.get(k).unwrap() == v)
342        );
343        assert!(
344            epoch_authorized_voters.len() == expected_authorized_voters.len()
345                && epoch_authorized_voters
346                    .iter()
347                    .all(|(k, v)| expected_authorized_voters.get(k).unwrap() == v)
348        );
349        assert_eq!(
350            total_stake,
351            num_nodes as u64 * num_vote_accounts_per_node as u64 * 100
352        );
353    }
354
355    fn create_test_stakes() -> Stakes<StakeAccount<Delegation>> {
356        let stakes_cache = StakesCache::new(Stakes::default());
357
358        let vote_pubkey = Pubkey::new_unique();
359        let vote_account = vote_state::create_account_with_authorized(
360            &Pubkey::new_unique(),
361            &Pubkey::new_unique(),
362            &Pubkey::new_unique(),
363            0,
364            1,
365        );
366
367        let stake = 1_000_000_000;
368        let stake_pubkey = Pubkey::new_unique();
369        let stake_account = stake_state::create_account(
370            &Pubkey::new_unique(),
371            &vote_pubkey,
372            &vote_account,
373            &Rent::default(),
374            stake,
375        );
376
377        stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
378        stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
379
380        let stakes = Stakes::clone(&stakes_cache.stakes());
381
382        stakes
383    }
384
385    #[test]
386    fn test_split_epoch_stakes_empty() {
387        let bank_epoch_stakes = HashMap::new();
388        let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
389        assert!(old.is_empty());
390        assert!(versioned.is_empty());
391    }
392
393    #[test]
394    fn test_split_epoch_stakes_delegations() {
395        let mut bank_epoch_stakes = HashMap::new();
396        let epoch = 0;
397        let stakes = Arc::new(StakesEnum::Delegations(create_test_stakes().into()));
398        let epoch_stakes = EpochStakes {
399            stakes,
400            total_stake: 100,
401            node_id_to_vote_accounts: Arc::new(HashMap::new()),
402            epoch_authorized_voters: Arc::new(HashMap::new()),
403        };
404        bank_epoch_stakes.insert(epoch, epoch_stakes.clone());
405
406        let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
407
408        assert_eq!(old.len(), 1);
409        assert_eq!(old.get(&epoch), Some(&epoch_stakes));
410        assert!(versioned.is_empty());
411    }
412
413    #[test]
414    fn test_split_epoch_stakes_accounts() {
415        let mut bank_epoch_stakes = HashMap::new();
416        let epoch = 0;
417        let test_stakes = create_test_stakes();
418        let stakes = Arc::new(StakesEnum::Accounts(test_stakes.clone()));
419        let epoch_stakes = EpochStakes {
420            stakes,
421            total_stake: 100,
422            node_id_to_vote_accounts: Arc::new(HashMap::new()),
423            epoch_authorized_voters: Arc::new(HashMap::new()),
424        };
425        bank_epoch_stakes.insert(epoch, epoch_stakes.clone());
426
427        let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
428
429        assert!(old.is_empty());
430        assert_eq!(versioned.len(), 1);
431        assert_eq!(
432            versioned.get(&epoch),
433            Some(&VersionedEpochStakes::Current {
434                stakes: SerdeStakesToStakeFormat::Account(test_stakes),
435                total_stake: epoch_stakes.total_stake,
436                node_id_to_vote_accounts: epoch_stakes.node_id_to_vote_accounts,
437                epoch_authorized_voters: epoch_stakes.epoch_authorized_voters,
438            })
439        );
440    }
441
442    #[test]
443    fn test_split_epoch_stakes_stakes() {
444        let mut bank_epoch_stakes = HashMap::new();
445        let epoch = 0;
446        let test_stakes: Stakes<Stake> = create_test_stakes().into();
447        let stakes = Arc::new(StakesEnum::Stakes(test_stakes.clone()));
448        let epoch_stakes = EpochStakes {
449            stakes,
450            total_stake: 100,
451            node_id_to_vote_accounts: Arc::new(HashMap::new()),
452            epoch_authorized_voters: Arc::new(HashMap::new()),
453        };
454        bank_epoch_stakes.insert(epoch, epoch_stakes.clone());
455
456        let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
457
458        assert!(old.is_empty());
459        assert_eq!(versioned.len(), 1);
460        assert_eq!(
461            versioned.get(&epoch),
462            Some(&VersionedEpochStakes::Current {
463                stakes: SerdeStakesToStakeFormat::Stake(test_stakes),
464                total_stake: epoch_stakes.total_stake,
465                node_id_to_vote_accounts: epoch_stakes.node_id_to_vote_accounts,
466                epoch_authorized_voters: epoch_stakes.epoch_authorized_voters,
467            })
468        );
469    }
470
471    #[test]
472    fn test_split_epoch_stakes_mixed() {
473        let mut bank_epoch_stakes = HashMap::new();
474
475        // Delegations
476        let epoch1 = 0;
477        let stakes1 = Arc::new(StakesEnum::Delegations(Stakes::default()));
478        let epoch_stakes1 = EpochStakes {
479            stakes: stakes1,
480            total_stake: 100,
481            node_id_to_vote_accounts: Arc::new(HashMap::new()),
482            epoch_authorized_voters: Arc::new(HashMap::new()),
483        };
484        bank_epoch_stakes.insert(epoch1, epoch_stakes1);
485
486        // Accounts
487        let epoch2 = 1;
488        let stakes2 = Arc::new(StakesEnum::Accounts(Stakes::default()));
489        let epoch_stakes2 = EpochStakes {
490            stakes: stakes2,
491            total_stake: 200,
492            node_id_to_vote_accounts: Arc::new(HashMap::new()),
493            epoch_authorized_voters: Arc::new(HashMap::new()),
494        };
495        bank_epoch_stakes.insert(epoch2, epoch_stakes2);
496
497        // Stakes
498        let epoch3 = 2;
499        let stakes3 = Arc::new(StakesEnum::Stakes(Stakes::default()));
500        let epoch_stakes3 = EpochStakes {
501            stakes: stakes3,
502            total_stake: 300,
503            node_id_to_vote_accounts: Arc::new(HashMap::new()),
504            epoch_authorized_voters: Arc::new(HashMap::new()),
505        };
506        bank_epoch_stakes.insert(epoch3, epoch_stakes3);
507
508        let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
509
510        assert_eq!(old.len(), 1);
511        assert!(old.contains_key(&epoch1));
512
513        assert_eq!(versioned.len(), 2);
514        assert_eq!(
515            versioned.get(&epoch2),
516            Some(&VersionedEpochStakes::Current {
517                stakes: SerdeStakesToStakeFormat::Account(Stakes::default()),
518                total_stake: 200,
519                node_id_to_vote_accounts: Arc::default(),
520                epoch_authorized_voters: Arc::default(),
521            })
522        );
523        assert_eq!(
524            versioned.get(&epoch3),
525            Some(&VersionedEpochStakes::Current {
526                stakes: SerdeStakesToStakeFormat::Stake(Stakes::default()),
527                total_stake: 300,
528                node_id_to_vote_accounts: Arc::default(),
529                epoch_authorized_voters: Arc::default(),
530            })
531        );
532    }
533
534    #[test]
535    fn test_node_id_to_stake() {
536        let num_nodes = 10;
537        let num_vote_accounts_per_node = 2;
538
539        let vote_accounts_map = new_vote_accounts(num_nodes, num_vote_accounts_per_node);
540        let node_id_to_stake_map = vote_accounts_map
541            .keys()
542            .enumerate()
543            .map(|(index, node_id)| (*node_id, ((index + 1) * 100) as u64))
544            .collect::<HashMap<_, _>>();
545        let epoch_vote_accounts = new_epoch_vote_accounts(&vote_accounts_map, |node_id| {
546            *node_id_to_stake_map.get(node_id).unwrap()
547        });
548        let epoch_stakes = EpochStakes::new_for_tests(epoch_vote_accounts, 0);
549
550        assert_eq!(epoch_stakes.total_stake(), 11000);
551        for (node_id, stake) in node_id_to_stake_map.iter() {
552            assert_eq!(
553                epoch_stakes.node_id_to_stake(node_id),
554                Some(*stake * num_vote_accounts_per_node as u64)
555            );
556        }
557    }
558}