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