solana_sysvar/
stake_history.rs1#[cfg(feature = "bincode")]
50use crate::Sysvar;
51#[deprecated(
52 since = "2.3.0",
53 note = "Use solana_sdk_ids::sysvar::stake_history instead"
54)]
55pub use solana_sdk_ids::sysvar::stake_history::{check_id, id, ID};
56#[deprecated(
57 since = "2.2.0",
58 note = "Use solana_stake_interface::stake_history instead"
59)]
60pub use solana_stake_interface::stake_history::{
61 StakeHistory, StakeHistoryEntry, StakeHistoryGetEntry, MAX_ENTRIES,
62};
63use {crate::get_sysvar, solana_clock::Epoch};
64
65#[cfg(feature = "bincode")]
66impl Sysvar for StakeHistory {
67 fn size_of() -> usize {
69 16392 }
72}
73
74#[deprecated(
76 since = "2.3.0",
77 note = "Use solana_stake_interface::sysvar::stake_history::StakeHistorySysvar in solana-stake-interface v2 instead"
78)]
79#[derive(Debug, PartialEq, Eq, Clone)]
80pub struct StakeHistorySysvar(pub Epoch);
81
82const EPOCH_AND_ENTRY_SERIALIZED_SIZE: u64 = 32;
84
85#[allow(deprecated)]
86impl StakeHistoryGetEntry for StakeHistorySysvar {
87 fn get_entry(&self, target_epoch: Epoch) -> Option<StakeHistoryEntry> {
88 let current_epoch = self.0;
89
90 let newest_historical_epoch = current_epoch.checked_sub(1)?;
92 let oldest_historical_epoch = current_epoch.saturating_sub(MAX_ENTRIES as u64);
93
94 if target_epoch < oldest_historical_epoch {
96 return None;
97 }
98
99 let epoch_delta = newest_historical_epoch.checked_sub(target_epoch)?;
102
103 let offset = epoch_delta
105 .checked_mul(EPOCH_AND_ENTRY_SERIALIZED_SIZE)?
106 .checked_add(std::mem::size_of::<u64>() as u64)?;
107
108 let mut entry_buf = [0; EPOCH_AND_ENTRY_SERIALIZED_SIZE as usize];
109 let result = get_sysvar(
110 &mut entry_buf,
111 &id(),
112 offset,
113 EPOCH_AND_ENTRY_SERIALIZED_SIZE,
114 );
115
116 match result {
117 Ok(()) => {
118 let entry_epoch = u64::from_le_bytes(entry_buf[0..8].try_into().unwrap());
120 let effective = u64::from_le_bytes(entry_buf[8..16].try_into().unwrap());
121 let activating = u64::from_le_bytes(entry_buf[16..24].try_into().unwrap());
122 let deactivating = u64::from_le_bytes(entry_buf[24..32].try_into().unwrap());
123
124 assert_eq!(entry_epoch, target_epoch);
126
127 Some(StakeHistoryEntry {
128 effective,
129 activating,
130 deactivating,
131 })
132 }
133 _ => None,
134 }
135 }
136}
137
138#[cfg(test)]
139#[allow(deprecated)]
140mod tests {
141 use {super::*, crate::tests::mock_get_sysvar_syscall, serial_test::serial};
142
143 #[test]
144 fn test_size_of() {
145 let mut stake_history = StakeHistory::default();
146 for i in 0..MAX_ENTRIES as u64 {
147 stake_history.add(
148 i,
149 StakeHistoryEntry {
150 activating: i,
151 ..StakeHistoryEntry::default()
152 },
153 );
154 }
155
156 assert_eq!(
157 bincode::serialized_size(&stake_history).unwrap() as usize,
158 StakeHistory::size_of()
159 );
160
161 let stake_history_inner: Vec<(Epoch, StakeHistoryEntry)> =
162 bincode::deserialize(&bincode::serialize(&stake_history).unwrap()).unwrap();
163 let epoch_entry = stake_history_inner.into_iter().next().unwrap();
164
165 assert_eq!(
166 bincode::serialized_size(&epoch_entry).unwrap(),
167 EPOCH_AND_ENTRY_SERIALIZED_SIZE
168 );
169 }
170
171 #[serial]
172 #[test]
173 fn test_stake_history_get_entry() {
174 let unique_entry_for_epoch = |epoch: u64| StakeHistoryEntry {
175 activating: epoch.saturating_mul(2),
176 deactivating: epoch.saturating_mul(3),
177 effective: epoch.saturating_mul(5),
178 };
179
180 let current_epoch = MAX_ENTRIES.saturating_add(2) as u64;
181
182 let mut stake_history = StakeHistory::default();
184 for i in 0..current_epoch {
185 stake_history.add(i, unique_entry_for_epoch(i));
186 }
187 assert_eq!(stake_history.len(), MAX_ENTRIES);
188 assert_eq!(stake_history.iter().map(|entry| entry.0).min().unwrap(), 2);
189
190 mock_get_sysvar_syscall(&bincode::serialize(&stake_history).unwrap());
192
193 let stake_history_sysvar = StakeHistorySysvar(current_epoch);
195
196 assert_eq!(stake_history.get(0), None);
199 assert_eq!(stake_history.get(1), None);
200 assert_eq!(stake_history.get(current_epoch), None);
201
202 assert_eq!(stake_history.get_entry(0), None);
203 assert_eq!(stake_history.get_entry(1), None);
204 assert_eq!(stake_history.get_entry(current_epoch), None);
205
206 assert_eq!(stake_history_sysvar.get_entry(0), None);
207 assert_eq!(stake_history_sysvar.get_entry(1), None);
208 assert_eq!(stake_history_sysvar.get_entry(current_epoch), None);
209
210 for i in 2..current_epoch {
211 let entry = Some(unique_entry_for_epoch(i));
212
213 assert_eq!(stake_history.get(i), entry.as_ref(),);
214
215 assert_eq!(stake_history.get_entry(i), entry,);
216
217 assert_eq!(stake_history_sysvar.get_entry(i), entry,);
218 }
219 }
220
221 #[serial]
222 #[test]
223 fn test_stake_history_get_entry_zero() {
224 let mut current_epoch = 0;
225
226 let stake_history = StakeHistory::default();
228 assert_eq!(stake_history.len(), 0);
229
230 mock_get_sysvar_syscall(&bincode::serialize(&stake_history).unwrap());
231 let stake_history_sysvar = StakeHistorySysvar(current_epoch);
232
233 assert_eq!(stake_history.get(0), None);
234 assert_eq!(stake_history.get_entry(0), None);
235 assert_eq!(stake_history_sysvar.get_entry(0), None);
236
237 let entry_zero = StakeHistoryEntry {
239 effective: 100,
240 ..StakeHistoryEntry::default()
241 };
242 let entry = Some(entry_zero.clone());
243
244 let mut stake_history = StakeHistory::default();
245 stake_history.add(current_epoch, entry_zero);
246 assert_eq!(stake_history.len(), 1);
247 current_epoch = current_epoch.saturating_add(1);
248
249 mock_get_sysvar_syscall(&bincode::serialize(&stake_history).unwrap());
250 let stake_history_sysvar = StakeHistorySysvar(current_epoch);
251
252 assert_eq!(stake_history.get(0), entry.as_ref());
253 assert_eq!(stake_history.get_entry(0), entry);
254 assert_eq!(stake_history_sysvar.get_entry(0), entry);
255
256 stake_history.add(current_epoch, StakeHistoryEntry::default());
258 assert_eq!(stake_history.len(), 2);
259 current_epoch = current_epoch.saturating_add(1);
260
261 mock_get_sysvar_syscall(&bincode::serialize(&stake_history).unwrap());
262 let stake_history_sysvar = StakeHistorySysvar(current_epoch);
263
264 assert_eq!(stake_history.get(0), entry.as_ref());
265 assert_eq!(stake_history.get_entry(0), entry);
266 assert_eq!(stake_history_sysvar.get_entry(0), entry);
267 }
268}