1use {
2 crate::stakes::{serde_stakes_to_delegation_format, SerdeStakesToStakeFormat, StakesEnum},
3 serde::{Deserialize, Serialize},
4 solana_sdk::{clock::Epoch, pubkey::Pubkey},
5 solana_vote::vote_account::VoteAccountsHashMap,
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 solana_vote::vote_account::VoteAccounts::from(Arc::new(vote_accounts_hash_map)),
52 im::HashMap::default(),
53 ))),
54 leader_schedule_epoch,
55 )
56 }
57
58 pub fn stakes(&self) -> &StakesEnum {
59 &self.stakes
60 }
61
62 pub fn total_stake(&self) -> u64 {
63 self.total_stake
64 }
65
66 pub fn set_total_stake(&mut self, total_stake: u64) {
68 self.total_stake = total_stake;
69 }
70
71 pub fn node_id_to_vote_accounts(&self) -> &Arc<NodeIdToVoteAccounts> {
72 &self.node_id_to_vote_accounts
73 }
74
75 pub fn node_id_to_stake(&self, node_id: &Pubkey) -> Option<u64> {
76 self.node_id_to_vote_accounts
77 .get(node_id)
78 .map(|x| x.total_stake)
79 }
80
81 pub fn epoch_authorized_voters(&self) -> &Arc<EpochAuthorizedVoters> {
82 &self.epoch_authorized_voters
83 }
84
85 pub fn vote_account_stake(&self, vote_account: &Pubkey) -> u64 {
86 self.stakes
87 .vote_accounts()
88 .get_delegated_stake(vote_account)
89 }
90
91 fn parse_epoch_vote_accounts(
92 epoch_vote_accounts: &VoteAccountsHashMap,
93 leader_schedule_epoch: Epoch,
94 ) -> (u64, NodeIdToVoteAccounts, EpochAuthorizedVoters) {
95 let mut node_id_to_vote_accounts: NodeIdToVoteAccounts = HashMap::new();
96 let total_stake = epoch_vote_accounts
97 .iter()
98 .map(|(_, (stake, _))| stake)
99 .sum();
100 let epoch_authorized_voters = epoch_vote_accounts
101 .iter()
102 .filter_map(|(key, (stake, account))| {
103 let vote_state = account.vote_state();
104
105 if *stake > 0 {
106 if let Some(authorized_voter) = vote_state
107 .authorized_voters()
108 .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_sdk::{account::AccountSharedData, rent::Rent},
233 solana_stake_program::stake_state::{self, Delegation, Stake},
234 solana_vote::vote_account::VoteAccount,
235 solana_vote_program::vote_state::{self, create_account_with_authorized},
236 std::iter,
237 };
238
239 struct VoteAccountInfo {
240 vote_account: Pubkey,
241 account: AccountSharedData,
242 authorized_voter: Pubkey,
243 }
244
245 fn new_vote_accounts(
246 num_nodes: usize,
247 num_vote_accounts_per_node: usize,
248 ) -> HashMap<Pubkey, Vec<VoteAccountInfo>> {
249 (0..num_nodes)
251 .map(|_| {
252 let node_id = solana_pubkey::new_rand();
253 (
254 node_id,
255 iter::repeat_with(|| {
256 let authorized_voter = solana_pubkey::new_rand();
257 VoteAccountInfo {
258 vote_account: solana_pubkey::new_rand(),
259 account: create_account_with_authorized(
260 &node_id,
261 &authorized_voter,
262 &node_id,
263 0,
264 100,
265 ),
266 authorized_voter,
267 }
268 })
269 .take(num_vote_accounts_per_node)
270 .collect(),
271 )
272 })
273 .collect()
274 }
275
276 fn new_epoch_vote_accounts(
277 vote_accounts_map: &HashMap<Pubkey, Vec<VoteAccountInfo>>,
278 node_id_to_stake_fn: impl Fn(&Pubkey) -> u64,
279 ) -> VoteAccountsHashMap {
280 vote_accounts_map
282 .iter()
283 .flat_map(|(node_id, vote_accounts)| {
284 vote_accounts.iter().map(|v| {
285 let vote_account = VoteAccount::try_from(v.account.clone()).unwrap();
286 (v.vote_account, (node_id_to_stake_fn(node_id), vote_account))
287 })
288 })
289 .collect()
290 }
291
292 #[test]
293 fn test_parse_epoch_vote_accounts() {
294 let stake_per_account = 100;
295 let num_vote_accounts_per_node = 2;
296 let num_nodes = 10;
297
298 let vote_accounts_map = new_vote_accounts(num_nodes, num_vote_accounts_per_node);
299
300 let expected_authorized_voters: HashMap<_, _> = vote_accounts_map
301 .iter()
302 .flat_map(|(_, vote_accounts)| {
303 vote_accounts
304 .iter()
305 .map(|v| (v.vote_account, v.authorized_voter))
306 })
307 .collect();
308
309 let expected_node_id_to_vote_accounts: HashMap<_, _> = vote_accounts_map
310 .iter()
311 .map(|(node_pubkey, vote_accounts)| {
312 let mut vote_accounts = vote_accounts
313 .iter()
314 .map(|v| (v.vote_account))
315 .collect::<Vec<_>>();
316 vote_accounts.sort();
317 let node_vote_accounts = NodeVoteAccounts {
318 vote_accounts,
319 total_stake: stake_per_account * num_vote_accounts_per_node as u64,
320 };
321 (*node_pubkey, node_vote_accounts)
322 })
323 .collect();
324
325 let epoch_vote_accounts =
326 new_epoch_vote_accounts(&vote_accounts_map, |_| stake_per_account);
327
328 let (total_stake, mut node_id_to_vote_accounts, epoch_authorized_voters) =
329 EpochStakes::parse_epoch_vote_accounts(&epoch_vote_accounts, 0);
330
331 node_id_to_vote_accounts
333 .iter_mut()
334 .for_each(|(_, node_vote_accounts)| node_vote_accounts.vote_accounts.sort());
335
336 assert!(
337 node_id_to_vote_accounts.len() == expected_node_id_to_vote_accounts.len()
338 && node_id_to_vote_accounts
339 .iter()
340 .all(|(k, v)| expected_node_id_to_vote_accounts.get(k).unwrap() == v)
341 );
342 assert!(
343 epoch_authorized_voters.len() == expected_authorized_voters.len()
344 && epoch_authorized_voters
345 .iter()
346 .all(|(k, v)| expected_authorized_voters.get(k).unwrap() == v)
347 );
348 assert_eq!(
349 total_stake,
350 num_nodes as u64 * num_vote_accounts_per_node as u64 * 100
351 );
352 }
353
354 fn create_test_stakes() -> Stakes<StakeAccount<Delegation>> {
355 let stakes_cache = StakesCache::new(Stakes::default());
356
357 let vote_pubkey = Pubkey::new_unique();
358 let vote_account = vote_state::create_account_with_authorized(
359 &Pubkey::new_unique(),
360 &Pubkey::new_unique(),
361 &Pubkey::new_unique(),
362 0,
363 1,
364 );
365
366 let stake = 1_000_000_000;
367 let stake_pubkey = Pubkey::new_unique();
368 let stake_account = stake_state::create_account(
369 &Pubkey::new_unique(),
370 &vote_pubkey,
371 &vote_account,
372 &Rent::default(),
373 stake,
374 );
375
376 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
377 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
378
379 let stakes = Stakes::clone(&stakes_cache.stakes());
380
381 stakes
382 }
383
384 #[test]
385 fn test_split_epoch_stakes_empty() {
386 let bank_epoch_stakes = HashMap::new();
387 let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
388 assert!(old.is_empty());
389 assert!(versioned.is_empty());
390 }
391
392 #[test]
393 fn test_split_epoch_stakes_delegations() {
394 let mut bank_epoch_stakes = HashMap::new();
395 let epoch = 0;
396 let stakes = Arc::new(StakesEnum::Delegations(create_test_stakes().into()));
397 let epoch_stakes = EpochStakes {
398 stakes,
399 total_stake: 100,
400 node_id_to_vote_accounts: Arc::new(HashMap::new()),
401 epoch_authorized_voters: Arc::new(HashMap::new()),
402 };
403 bank_epoch_stakes.insert(epoch, epoch_stakes.clone());
404
405 let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
406
407 assert_eq!(old.len(), 1);
408 assert_eq!(old.get(&epoch), Some(&epoch_stakes));
409 assert!(versioned.is_empty());
410 }
411
412 #[test]
413 fn test_split_epoch_stakes_accounts() {
414 let mut bank_epoch_stakes = HashMap::new();
415 let epoch = 0;
416 let test_stakes = create_test_stakes();
417 let stakes = Arc::new(StakesEnum::Accounts(test_stakes.clone()));
418 let epoch_stakes = EpochStakes {
419 stakes,
420 total_stake: 100,
421 node_id_to_vote_accounts: Arc::new(HashMap::new()),
422 epoch_authorized_voters: Arc::new(HashMap::new()),
423 };
424 bank_epoch_stakes.insert(epoch, epoch_stakes.clone());
425
426 let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
427
428 assert!(old.is_empty());
429 assert_eq!(versioned.len(), 1);
430 assert_eq!(
431 versioned.get(&epoch),
432 Some(&VersionedEpochStakes::Current {
433 stakes: SerdeStakesToStakeFormat::Account(test_stakes),
434 total_stake: epoch_stakes.total_stake,
435 node_id_to_vote_accounts: epoch_stakes.node_id_to_vote_accounts,
436 epoch_authorized_voters: epoch_stakes.epoch_authorized_voters,
437 })
438 );
439 }
440
441 #[test]
442 fn test_split_epoch_stakes_stakes() {
443 let mut bank_epoch_stakes = HashMap::new();
444 let epoch = 0;
445 let test_stakes: Stakes<Stake> = create_test_stakes().into();
446 let stakes = Arc::new(StakesEnum::Stakes(test_stakes.clone()));
447 let epoch_stakes = EpochStakes {
448 stakes,
449 total_stake: 100,
450 node_id_to_vote_accounts: Arc::new(HashMap::new()),
451 epoch_authorized_voters: Arc::new(HashMap::new()),
452 };
453 bank_epoch_stakes.insert(epoch, epoch_stakes.clone());
454
455 let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
456
457 assert!(old.is_empty());
458 assert_eq!(versioned.len(), 1);
459 assert_eq!(
460 versioned.get(&epoch),
461 Some(&VersionedEpochStakes::Current {
462 stakes: SerdeStakesToStakeFormat::Stake(test_stakes),
463 total_stake: epoch_stakes.total_stake,
464 node_id_to_vote_accounts: epoch_stakes.node_id_to_vote_accounts,
465 epoch_authorized_voters: epoch_stakes.epoch_authorized_voters,
466 })
467 );
468 }
469
470 #[test]
471 fn test_split_epoch_stakes_mixed() {
472 let mut bank_epoch_stakes = HashMap::new();
473
474 let epoch1 = 0;
476 let stakes1 = Arc::new(StakesEnum::Delegations(Stakes::default()));
477 let epoch_stakes1 = EpochStakes {
478 stakes: stakes1,
479 total_stake: 100,
480 node_id_to_vote_accounts: Arc::new(HashMap::new()),
481 epoch_authorized_voters: Arc::new(HashMap::new()),
482 };
483 bank_epoch_stakes.insert(epoch1, epoch_stakes1);
484
485 let epoch2 = 1;
487 let stakes2 = Arc::new(StakesEnum::Accounts(Stakes::default()));
488 let epoch_stakes2 = EpochStakes {
489 stakes: stakes2,
490 total_stake: 200,
491 node_id_to_vote_accounts: Arc::new(HashMap::new()),
492 epoch_authorized_voters: Arc::new(HashMap::new()),
493 };
494 bank_epoch_stakes.insert(epoch2, epoch_stakes2);
495
496 let epoch3 = 2;
498 let stakes3 = Arc::new(StakesEnum::Stakes(Stakes::default()));
499 let epoch_stakes3 = EpochStakes {
500 stakes: stakes3,
501 total_stake: 300,
502 node_id_to_vote_accounts: Arc::new(HashMap::new()),
503 epoch_authorized_voters: Arc::new(HashMap::new()),
504 };
505 bank_epoch_stakes.insert(epoch3, epoch_stakes3);
506
507 let (old, versioned) = split_epoch_stakes(bank_epoch_stakes);
508
509 assert_eq!(old.len(), 1);
510 assert!(old.contains_key(&epoch1));
511
512 assert_eq!(versioned.len(), 2);
513 assert_eq!(
514 versioned.get(&epoch2),
515 Some(&VersionedEpochStakes::Current {
516 stakes: SerdeStakesToStakeFormat::Account(Stakes::default()),
517 total_stake: 200,
518 node_id_to_vote_accounts: Arc::default(),
519 epoch_authorized_voters: Arc::default(),
520 })
521 );
522 assert_eq!(
523 versioned.get(&epoch3),
524 Some(&VersionedEpochStakes::Current {
525 stakes: SerdeStakesToStakeFormat::Stake(Stakes::default()),
526 total_stake: 300,
527 node_id_to_vote_accounts: Arc::default(),
528 epoch_authorized_voters: Arc::default(),
529 })
530 );
531 }
532
533 #[test]
534 fn test_node_id_to_stake() {
535 let num_nodes = 10;
536 let num_vote_accounts_per_node = 2;
537
538 let vote_accounts_map = new_vote_accounts(num_nodes, num_vote_accounts_per_node);
539 let node_id_to_stake_map = vote_accounts_map
540 .keys()
541 .enumerate()
542 .map(|(index, node_id)| (*node_id, ((index + 1) * 100) as u64))
543 .collect::<HashMap<_, _>>();
544 let epoch_vote_accounts = new_epoch_vote_accounts(&vote_accounts_map, |node_id| {
545 *node_id_to_stake_map.get(node_id).unwrap()
546 });
547 let epoch_stakes = EpochStakes::new_for_tests(epoch_vote_accounts, 0);
548
549 assert_eq!(epoch_stakes.total_stake(), 11000);
550 for (node_id, stake) in node_id_to_stake_map.iter() {
551 assert_eq!(
552 epoch_stakes.node_id_to_stake(node_id),
553 Some(*stake * num_vote_accounts_per_node as u64)
554 );
555 }
556 }
557}