1use {
2 crate::{
3 cluster_info_vote_listener::SlotVoteTracker,
4 cluster_slots_service::slot_supporters::SlotSupporters,
5 consensus::{Stake, ThresholdDecision, VotedStakes},
6 replay_stage::SUPERMINORITY_THRESHOLD,
7 },
8 solana_clock::Slot,
9 solana_hash::Hash,
10 solana_ledger::blockstore_processor::{
11 AsyncVerificationProgress, ConfirmationProgress, ReplaySlotStats,
12 },
13 solana_pubkey::Pubkey,
14 solana_runtime::{bank::Bank, bank_forks::BankForks},
15 solana_vote::vote_account::VoteAccountsHashMap,
16 std::{
17 collections::{HashMap, HashSet},
18 sync::{Arc, RwLock},
19 time::Instant,
20 },
21};
22
23type VotedSlot = Slot;
24type ExpirationSlot = Slot;
25
26#[derive(Clone, Copy, Debug)]
27pub struct LockoutInterval {
28 pub voter: Pubkey,
29 pub start: VotedSlot,
30 pub end: ExpirationSlot,
31}
32
33pub type LockoutIntervals = Vec<LockoutInterval>;
34
35#[derive(Debug)]
36pub struct ValidatorStakeInfo {
37 pub validator_vote_pubkey: Pubkey,
38 pub stake: u64,
39 pub total_epoch_stake: u64,
40}
41
42impl Default for ValidatorStakeInfo {
43 fn default() -> Self {
44 Self {
45 stake: 0,
46 validator_vote_pubkey: Pubkey::default(),
47 total_epoch_stake: 1,
48 }
49 }
50}
51
52impl ValidatorStakeInfo {
53 pub fn new(validator_vote_pubkey: Pubkey, stake: u64, total_epoch_stake: u64) -> Self {
54 Self {
55 validator_vote_pubkey,
56 stake,
57 total_epoch_stake,
58 }
59 }
60}
61
62pub const RETRANSMIT_BASE_DELAY_MS: u64 = 5_000;
63pub const RETRANSMIT_BACKOFF_CAP: u32 = 6;
64
65#[derive(Debug)]
66pub struct RetransmitInfo {
67 pub(crate) retry_time: Instant,
68 pub(crate) retry_iteration: u32,
69}
70
71impl RetransmitInfo {
72 pub fn reached_retransmit_threshold(&self) -> bool {
73 let backoff = std::cmp::min(self.retry_iteration, RETRANSMIT_BACKOFF_CAP);
74 let backoff_duration_ms = (1_u64 << backoff) * RETRANSMIT_BASE_DELAY_MS;
75 self.retry_time.elapsed().as_millis() > u128::from(backoff_duration_ms)
76 }
77
78 pub fn increment_retry_iteration(&mut self) {
79 self.retry_iteration = self.retry_iteration.saturating_add(1);
80 self.retry_time = Instant::now();
81 }
82}
83
84#[derive(Clone, Debug, Eq, PartialEq)]
85pub enum DeadSlotReason {
86 Hard,
88 ReplayFailureBeforeUpdateParent,
91}
92
93pub struct ForkProgress {
94 pub dead_reason: Option<DeadSlotReason>,
95 pub fork_stats: ForkStats,
96 pub propagated_stats: PropagatedStats,
97 pub replay_stats: Arc<RwLock<ReplaySlotStats>>,
98 pub replay_progress: Arc<RwLock<ConfirmationProgress>>,
99 pub retransmit_info: RetransmitInfo,
100 pub num_blocks_on_fork: u64,
105 pub num_dropped_blocks_on_fork: u64,
106}
107
108impl ForkProgress {
109 pub fn new(
110 last_entry: Hash,
111 prev_leader_slot: Option<Slot>,
112 validator_stake_info: Option<ValidatorStakeInfo>,
113 num_blocks_on_fork: u64,
114 num_dropped_blocks_on_fork: u64,
115 async_verification: Option<AsyncVerificationProgress>,
116 ) -> Self {
117 let (
118 is_leader_slot,
119 propagated_validators_stake,
120 propagated_validators,
121 is_propagated,
122 total_epoch_stake,
123 ) = validator_stake_info
124 .map(|info| {
125 (
126 true,
127 info.stake,
128 vec![info.validator_vote_pubkey].into_iter().collect(),
129 {
130 if info.total_epoch_stake == 0 {
131 true
132 } else {
133 info.stake as f64 / info.total_epoch_stake as f64
134 > SUPERMINORITY_THRESHOLD
135 }
136 },
137 info.total_epoch_stake,
138 )
139 })
140 .unwrap_or((false, 0, HashSet::new(), false, 0));
141
142 Self {
143 dead_reason: None,
144 fork_stats: ForkStats::default(),
145 replay_stats: Arc::new(RwLock::new(ReplaySlotStats::default())),
146 replay_progress: Arc::new(RwLock::new(
147 ConfirmationProgress::new_with_async_verification(last_entry, async_verification),
148 )),
149 num_blocks_on_fork,
150 num_dropped_blocks_on_fork,
151 propagated_stats: PropagatedStats {
152 propagated_validators,
153 propagated_validators_stake,
154 is_propagated,
155 is_leader_slot,
156 prev_leader_slot,
157 total_epoch_stake,
158 ..PropagatedStats::default()
159 },
160 retransmit_info: RetransmitInfo {
161 retry_time: Instant::now(),
162 retry_iteration: 0u32,
163 },
164 }
165 }
166
167 pub fn new_from_bank(
168 bank: &Bank,
169 validator_identity: &Pubkey,
170 validator_vote_pubkey: &Pubkey,
171 prev_leader_slot: Option<Slot>,
172 num_blocks_on_fork: u64,
173 num_dropped_blocks_on_fork: u64,
174 async_verification: Option<AsyncVerificationProgress>,
175 ) -> Self {
176 let validator_stake_info = {
177 if bank.leader_id() == validator_identity {
178 Some(ValidatorStakeInfo::new(
179 *validator_vote_pubkey,
180 bank.epoch_vote_account_stake(validator_vote_pubkey),
181 bank.total_epoch_stake(),
182 ))
183 } else {
184 None
185 }
186 };
187
188 let mut new_progress = Self::new(
189 bank.last_blockhash(),
190 prev_leader_slot,
191 validator_stake_info,
192 num_blocks_on_fork,
193 num_dropped_blocks_on_fork,
194 async_verification,
195 );
196
197 if bank.is_frozen() {
198 new_progress.fork_stats.bank_hash = Some(bank.hash());
199 }
200 new_progress
201 }
202
203 pub fn mark_dead(&mut self, reason: DeadSlotReason) {
204 self.dead_reason = Some(reason);
205 }
206}
207
208#[derive(Debug, Clone, Default)]
209pub struct ForkStats {
210 pub fork_stake: Stake,
211 pub total_stake: Stake,
212 pub block_height: u64,
213 pub has_voted: bool,
214 pub is_recent: bool,
215 pub is_empty: bool,
216 pub vote_threshold: Vec<ThresholdDecision>,
217 pub is_locked_out: bool,
218 pub voted_stakes: VotedStakes,
219 pub duplicate_confirmed_hash: Option<Hash>,
220 pub computed: bool,
221 pub lockout_intervals: LockoutIntervals,
222 pub bank_hash: Option<Hash>,
223 pub my_latest_landed_vote: Option<Slot>,
224}
225
226impl ForkStats {
227 pub fn fork_weight(&self) -> f64 {
229 self.fork_stake as f64 / self.total_stake as f64
230 }
231}
232
233#[derive(Clone, Default)]
234pub struct PropagatedStats {
235 pub propagated_validators: HashSet<Pubkey>,
236 pub propagated_node_ids: HashSet<Pubkey>,
237 pub propagated_validators_stake: u64,
238 pub is_propagated: bool,
239 pub is_leader_slot: bool,
240 pub prev_leader_slot: Option<Slot>,
241 pub slot_vote_tracker: Option<Arc<RwLock<SlotVoteTracker>>>,
242 pub cluster_slot_pubkeys: Option<Arc<SlotSupporters>>,
243 pub total_epoch_stake: u64,
244}
245
246impl PropagatedStats {
247 pub fn add_vote_pubkey(&mut self, vote_pubkey: Pubkey, stake: u64) {
248 if self.propagated_validators.insert(vote_pubkey) {
249 self.propagated_validators_stake += stake;
250 }
251 }
252
253 pub fn add_node_pubkey(&mut self, node_pubkey: &Pubkey, bank: &Bank) {
254 if !self.propagated_node_ids.contains(node_pubkey) {
255 let node_vote_accounts = bank
256 .epoch_vote_accounts_for_node_id(node_pubkey)
257 .map(|v| &v.vote_accounts);
258
259 if let Some(node_vote_accounts) = node_vote_accounts {
260 self.add_node_pubkey_internal(
261 node_pubkey,
262 node_vote_accounts,
263 bank.epoch_vote_accounts(bank.epoch())
264 .expect("Epoch stakes for bank's own epoch must exist"),
265 );
266 }
267 }
268 }
269
270 fn add_node_pubkey_internal(
271 &mut self,
272 node_pubkey: &Pubkey,
273 vote_account_pubkeys: &[Pubkey],
274 epoch_vote_accounts: &VoteAccountsHashMap,
275 ) {
276 self.propagated_node_ids.insert(*node_pubkey);
277 for vote_account_pubkey in vote_account_pubkeys.iter() {
278 let stake = epoch_vote_accounts
279 .get(vote_account_pubkey)
280 .map(|(stake, _)| *stake)
281 .unwrap_or(0);
282 self.add_vote_pubkey(*vote_account_pubkey, stake);
283 }
284 }
285}
286
287#[derive(Default)]
288pub struct ProgressMap {
289 progress_map: HashMap<Slot, ForkProgress>,
290 bank_switch_counts: HashMap<Slot, u64>,
293}
294
295impl std::ops::Deref for ProgressMap {
296 type Target = HashMap<Slot, ForkProgress>;
297 fn deref(&self) -> &Self::Target {
298 &self.progress_map
299 }
300}
301
302impl std::ops::DerefMut for ProgressMap {
303 fn deref_mut(&mut self) -> &mut Self::Target {
304 &mut self.progress_map
305 }
306}
307
308impl ProgressMap {
309 pub fn insert(&mut self, slot: Slot, fork_progress: ForkProgress) {
310 let num_bank_switches = self.get_num_bank_switches(slot);
311 fork_progress
312 .replay_stats
313 .write()
314 .unwrap()
315 .num_bank_switches = num_bank_switches;
316 self.progress_map.insert(slot, fork_progress);
317 }
318
319 pub fn get_propagated_stats(&self, slot: Slot) -> Option<&PropagatedStats> {
320 self.progress_map
321 .get(&slot)
322 .map(|fork_progress| &fork_progress.propagated_stats)
323 }
324
325 pub fn get_propagated_stats_mut(&mut self, slot: Slot) -> Option<&mut PropagatedStats> {
326 self.progress_map
327 .get_mut(&slot)
328 .map(|fork_progress| &mut fork_progress.propagated_stats)
329 }
330
331 pub fn get_propagated_stats_must_exist(&self, slot: Slot) -> &PropagatedStats {
332 self.get_propagated_stats(slot)
333 .unwrap_or_else(|| panic!("slot={slot} must exist in ProgressMap"))
334 }
335
336 pub fn get_fork_stats(&self, slot: Slot) -> Option<&ForkStats> {
337 self.progress_map
338 .get(&slot)
339 .map(|fork_progress| &fork_progress.fork_stats)
340 }
341
342 pub fn increment_num_bank_switches(&mut self, slot: Slot) {
343 let count = self.bank_switch_counts.entry(slot).or_insert(0);
344 *count = count.saturating_add(1);
345 }
346
347 pub fn get_num_bank_switches(&self, slot: Slot) -> u64 {
348 self.bank_switch_counts.get(&slot).cloned().unwrap_or(0)
349 }
350
351 pub fn get_fork_stats_mut(&mut self, slot: Slot) -> Option<&mut ForkStats> {
352 self.progress_map
353 .get_mut(&slot)
354 .map(|fork_progress| &mut fork_progress.fork_stats)
355 }
356
357 pub fn get_retransmit_info(&self, slot: Slot) -> Option<&RetransmitInfo> {
358 self.progress_map
359 .get(&slot)
360 .map(|fork_progress| &fork_progress.retransmit_info)
361 }
362
363 pub fn get_retransmit_info_mut(&mut self, slot: Slot) -> Option<&mut RetransmitInfo> {
364 self.progress_map
365 .get_mut(&slot)
366 .map(|fork_progress| &mut fork_progress.retransmit_info)
367 }
368
369 pub fn is_dead(&self, slot: Slot) -> Option<bool> {
370 self.progress_map
371 .get(&slot)
372 .map(|fork_progress| fork_progress.dead_reason.is_some())
373 }
374
375 pub fn dead_reason(&self, slot: Slot) -> Option<&DeadSlotReason> {
376 self.progress_map
377 .get(&slot)
378 .and_then(|fork_progress| fork_progress.dead_reason.as_ref())
379 }
380
381 pub fn get_hash(&self, slot: Slot) -> Option<Hash> {
382 self.progress_map
383 .get(&slot)
384 .and_then(|fork_progress| fork_progress.fork_stats.bank_hash)
385 }
386
387 pub fn is_propagated(&self, slot: Slot) -> Option<bool> {
388 self.get_propagated_stats(slot)
389 .map(|stats| stats.is_propagated)
390 }
391
392 pub fn get_latest_leader_slot_must_exist(&self, slot: Slot) -> Option<Slot> {
393 let propagated_stats = self.get_propagated_stats_must_exist(slot);
394 if propagated_stats.is_leader_slot {
395 Some(slot)
396 } else {
397 propagated_stats.prev_leader_slot
398 }
399 }
400
401 pub fn get_leader_propagation_slot_must_exist(&self, slot: Slot) -> (bool, Option<Slot>) {
402 if let Some(leader_slot) = self.get_latest_leader_slot_must_exist(slot) {
403 (
407 self.is_propagated(leader_slot).unwrap_or(true),
408 Some(leader_slot),
409 )
410 } else {
411 (true, None)
416 }
417 }
418
419 pub fn my_latest_landed_vote(&self, slot: Slot) -> Option<Slot> {
420 self.progress_map
421 .get(&slot)
422 .and_then(|s| s.fork_stats.my_latest_landed_vote)
423 }
424
425 pub fn set_duplicate_confirmed_hash(&mut self, slot: Slot, hash: Hash) {
426 let slot_progress = self.get_mut(&slot).unwrap();
427 slot_progress.fork_stats.duplicate_confirmed_hash = Some(hash);
428 }
429
430 pub fn is_duplicate_confirmed(&self, slot: Slot) -> Option<bool> {
431 self.progress_map
432 .get(&slot)
433 .map(|s| s.fork_stats.duplicate_confirmed_hash.is_some())
434 }
435
436 pub fn get_bank_prev_leader_slot(&self, bank: &Bank) -> Option<Slot> {
437 let parent_slot = bank.parent_slot();
438 self.get_propagated_stats(parent_slot)
439 .map(|stats| {
440 if stats.is_leader_slot {
441 Some(parent_slot)
442 } else {
443 stats.prev_leader_slot
444 }
445 })
446 .unwrap_or(None)
447 }
448
449 pub fn handle_new_root(&mut self, bank_forks: &BankForks) {
450 self.progress_map
451 .retain(|k, _| bank_forks.get(*k).is_some());
452 self.bank_switch_counts
453 .retain(|k, _| bank_forks.get(*k).is_some());
454 }
455
456 pub fn log_propagated_stats(&self, slot: Slot, bank_forks: &RwLock<BankForks>) {
457 if let Some(stats) = self.get_propagated_stats(slot) {
458 info!(
459 "Propagated stats: total staked: {}, observed staked: {}, vote pubkeys: {:?}, \
460 node_pubkeys: {:?}, slot: {slot}, epoch: {:?}",
461 stats.total_epoch_stake,
462 stats.propagated_validators_stake,
463 stats.propagated_validators,
464 stats.propagated_node_ids,
465 bank_forks.read().unwrap().get(slot).map(|x| x.epoch()),
466 );
467 }
468 }
469}
470
471#[cfg(test)]
472mod test {
473 use {super::*, solana_vote::vote_account::VoteAccount};
474
475 #[test]
476 fn test_add_vote_pubkey() {
477 let mut stats = PropagatedStats::default();
478 let mut vote_pubkey = solana_pubkey::new_rand();
479
480 stats.add_vote_pubkey(vote_pubkey, 1);
483 assert!(stats.propagated_validators.contains(&vote_pubkey));
484 assert_eq!(stats.propagated_validators_stake, 1);
485
486 stats.add_vote_pubkey(vote_pubkey, 1);
488 assert!(stats.propagated_validators.contains(&vote_pubkey));
489 assert_eq!(stats.propagated_validators_stake, 1);
490
491 vote_pubkey = solana_pubkey::new_rand();
493 stats.add_vote_pubkey(vote_pubkey, 2);
494 assert!(stats.propagated_validators.contains(&vote_pubkey));
495 assert_eq!(stats.propagated_validators_stake, 3);
496 }
497
498 #[test]
499 fn test_add_node_pubkey_internal() {
500 let num_vote_accounts = 10;
501 let staked_vote_accounts = 5;
502 let vote_account_pubkeys: Vec<_> = std::iter::repeat_with(solana_pubkey::new_rand)
503 .take(num_vote_accounts)
504 .collect();
505 let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
506 .iter()
507 .skip(num_vote_accounts - staked_vote_accounts)
508 .map(|pubkey| (*pubkey, (1, VoteAccount::new_random())))
509 .collect();
510
511 let mut stats = PropagatedStats::default();
512 let mut node_pubkey = solana_pubkey::new_rand();
513
514 stats.add_node_pubkey_internal(&node_pubkey, &vote_account_pubkeys, &epoch_vote_accounts);
517 assert!(stats.propagated_node_ids.contains(&node_pubkey));
518 assert_eq!(
519 stats.propagated_validators_stake,
520 staked_vote_accounts as u64
521 );
522
523 stats.add_node_pubkey_internal(&node_pubkey, &vote_account_pubkeys, &epoch_vote_accounts);
525 assert!(stats.propagated_node_ids.contains(&node_pubkey));
526 assert_eq!(
527 stats.propagated_validators_stake,
528 staked_vote_accounts as u64
529 );
530
531 node_pubkey = solana_pubkey::new_rand();
534 stats.add_node_pubkey_internal(&node_pubkey, &vote_account_pubkeys, &epoch_vote_accounts);
535 assert!(stats.propagated_node_ids.contains(&node_pubkey));
536 assert_eq!(
537 stats.propagated_validators_stake,
538 staked_vote_accounts as u64
539 );
540
541 node_pubkey = solana_pubkey::new_rand();
544 let vote_account_pubkeys: Vec<_> = std::iter::repeat_with(solana_pubkey::new_rand)
545 .take(num_vote_accounts)
546 .collect();
547 let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
548 .iter()
549 .skip(num_vote_accounts - staked_vote_accounts)
550 .map(|pubkey| (*pubkey, (1, VoteAccount::new_random())))
551 .collect();
552 stats.add_node_pubkey_internal(&node_pubkey, &vote_account_pubkeys, &epoch_vote_accounts);
553 assert!(stats.propagated_node_ids.contains(&node_pubkey));
554 assert_eq!(
555 stats.propagated_validators_stake,
556 2 * staked_vote_accounts as u64
557 );
558 }
559
560 #[test]
561 fn test_is_propagated_status_on_construction() {
562 let progress = ForkProgress::new(Hash::default(), Some(9), None, 0, 0, None);
565 assert!(!progress.propagated_stats.is_propagated);
566
567 let progress = ForkProgress::new(
569 Hash::default(),
570 Some(9),
571 Some(ValidatorStakeInfo {
572 total_epoch_stake: 0,
573 ..ValidatorStakeInfo::default()
574 }),
575 0,
576 0,
577 None,
578 );
579 assert!(progress.propagated_stats.is_propagated);
580
581 let progress = ForkProgress::new(
584 Hash::default(),
585 Some(9),
586 Some(ValidatorStakeInfo {
587 total_epoch_stake: 2,
588 ..ValidatorStakeInfo::default()
589 }),
590 0,
591 0,
592 None,
593 );
594 assert!(!progress.propagated_stats.is_propagated);
595
596 let progress = ForkProgress::new(
598 Hash::default(),
599 Some(9),
600 Some(ValidatorStakeInfo {
601 stake: 1,
602 total_epoch_stake: 2,
603 ..ValidatorStakeInfo::default()
604 }),
605 0,
606 0,
607 None,
608 );
609 assert!(progress.propagated_stats.is_propagated);
610
611 let progress = ForkProgress::new(
615 Hash::default(),
616 Some(9),
617 Some(ValidatorStakeInfo::default()),
618 0,
619 0,
620 None,
621 );
622 assert!(!progress.propagated_stats.is_propagated);
623 }
624
625 #[test]
626 fn test_is_propagated() {
627 let mut progress_map = ProgressMap::default();
628
629 progress_map.insert(
632 10,
633 ForkProgress::new(Hash::default(), Some(9), None, 0, 0, None),
634 );
635 progress_map.insert(
636 9,
637 ForkProgress::new(
638 Hash::default(),
639 None,
640 Some(ValidatorStakeInfo::default()),
641 0,
642 0,
643 None,
644 ),
645 );
646
647 assert!(!progress_map.get_leader_propagation_slot_must_exist(9).0);
649 assert!(!progress_map.get_leader_propagation_slot_must_exist(10).0);
650
651 progress_map.insert(
656 8,
657 ForkProgress::new(Hash::default(), Some(7), None, 0, 0, None),
658 );
659 assert!(progress_map.get_leader_propagation_slot_must_exist(8).0);
660
661 progress_map
663 .get_propagated_stats_mut(9)
664 .unwrap()
665 .is_propagated = true;
666 assert!(progress_map.get_leader_propagation_slot_must_exist(9).0);
667 assert!(progress_map.get(&9).unwrap().propagated_stats.is_propagated);
668
669 assert!(progress_map.get_leader_propagation_slot_must_exist(10).0);
672
673 progress_map
676 .get_propagated_stats_mut(10)
677 .unwrap()
678 .is_leader_slot = true;
679 assert!(!progress_map.get_leader_propagation_slot_must_exist(10).0);
680 }
681}