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