1use {
3 crate::{program_error::ProgramError, stake::MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION},
4 solana_clock::Epoch,
5};
6
7pub fn get_minimum_delegation() -> Result<u64, ProgramError> {
15 let instruction = super::instruction::get_minimum_delegation();
16 crate::program::invoke_unchecked(&instruction, &[])?;
17 get_minimum_delegation_return_data()
18}
19
20fn get_minimum_delegation_return_data() -> Result<u64, ProgramError> {
28 crate::program::get_return_data()
29 .ok_or(ProgramError::InvalidInstructionData)
30 .and_then(|(program_id, return_data)| {
31 (program_id == super::program::id())
32 .then_some(return_data)
33 .ok_or(ProgramError::IncorrectProgramId)
34 })
35 .and_then(|return_data| {
36 return_data
37 .try_into()
38 .or(Err(ProgramError::InvalidInstructionData))
39 })
40 .map(u64::from_le_bytes)
41}
42
43pub fn acceptable_reference_epoch_credits(
46 epoch_credits: &[(Epoch, u64, u64)],
47 current_epoch: Epoch,
48) -> bool {
49 if let Some(epoch_index) = epoch_credits
50 .len()
51 .checked_sub(MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION)
52 {
53 let mut epoch = current_epoch;
54 for (vote_epoch, ..) in epoch_credits[epoch_index..].iter().rev() {
55 if *vote_epoch != epoch {
56 return false;
57 }
58 epoch = epoch.saturating_sub(1);
59 }
60 true
61 } else {
62 false
63 }
64}
65
66pub fn eligible_for_deactivate_delinquent(
69 epoch_credits: &[(Epoch, u64, u64)],
70 current_epoch: Epoch,
71) -> bool {
72 match epoch_credits.last() {
73 None => true,
74 Some((epoch, ..)) => {
75 if let Some(minimum_epoch) =
76 current_epoch.checked_sub(MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION as Epoch)
77 {
78 *epoch <= minimum_epoch
79 } else {
80 false
81 }
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_acceptable_reference_epoch_credits() {
92 let epoch_credits = [];
93 assert!(!acceptable_reference_epoch_credits(&epoch_credits, 0));
94
95 let epoch_credits = [(0, 42, 42), (1, 42, 42), (2, 42, 42), (3, 42, 42)];
96 assert!(!acceptable_reference_epoch_credits(&epoch_credits, 3));
97
98 let epoch_credits = [
99 (0, 42, 42),
100 (1, 42, 42),
101 (2, 42, 42),
102 (3, 42, 42),
103 (4, 42, 42),
104 ];
105 assert!(!acceptable_reference_epoch_credits(&epoch_credits, 3));
106 assert!(acceptable_reference_epoch_credits(&epoch_credits, 4));
107
108 let epoch_credits = [
109 (1, 42, 42),
110 (2, 42, 42),
111 (3, 42, 42),
112 (4, 42, 42),
113 (5, 42, 42),
114 ];
115 assert!(acceptable_reference_epoch_credits(&epoch_credits, 5));
116
117 let epoch_credits = [
118 (0, 42, 42),
119 (2, 42, 42),
120 (3, 42, 42),
121 (4, 42, 42),
122 (5, 42, 42),
123 ];
124 assert!(!acceptable_reference_epoch_credits(&epoch_credits, 5));
125 }
126
127 #[test]
128 fn test_eligible_for_deactivate_delinquent() {
129 let epoch_credits = [];
130 assert!(eligible_for_deactivate_delinquent(&epoch_credits, 42));
131
132 let epoch_credits = [(0, 42, 42)];
133 assert!(!eligible_for_deactivate_delinquent(&epoch_credits, 0));
134
135 let epoch_credits = [(0, 42, 42)];
136 assert!(!eligible_for_deactivate_delinquent(
137 &epoch_credits,
138 MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION as Epoch - 1
139 ));
140 assert!(eligible_for_deactivate_delinquent(
141 &epoch_credits,
142 MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION as Epoch
143 ));
144
145 let epoch_credits = [(100, 42, 42)];
146 assert!(!eligible_for_deactivate_delinquent(
147 &epoch_credits,
148 100 + MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION as Epoch - 1
149 ));
150 assert!(eligible_for_deactivate_delinquent(
151 &epoch_credits,
152 100 + MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION as Epoch
153 ));
154 }
155}