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 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
164pub(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 (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 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 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 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 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 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}