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