1#[cfg(feature = "bytemuck")]
50use bytemuck_derive::{Pod, Zeroable};
51#[cfg(feature = "bincode")]
52use {crate::Sysvar, clone_solana_account_info::AccountInfo};
53use {clone_solana_clock::Slot, clone_solana_hash::Hash};
54
55#[cfg(all(feature = "bincode", feature = "bytemuck"))]
56const U64_SIZE: usize = std::mem::size_of::<u64>();
57
58pub use {
59 clone_solana_sdk_ids::sysvar::slot_hashes::{check_id, id, ID},
60 clone_solana_slot_hashes::SlotHashes,
61 clone_solana_sysvar_id::SysvarId,
62};
63
64#[cfg(feature = "bincode")]
65impl Sysvar for SlotHashes {
66 fn size_of() -> usize {
68 20_488 }
71 fn from_account_info(
72 _account_info: &AccountInfo,
73 ) -> Result<Self, clone_solana_program_error::ProgramError> {
74 Err(clone_solana_program_error::ProgramError::UnsupportedSysvar)
76 }
77}
78
79#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
81#[derive(Copy, Clone, Default)]
82#[repr(C)]
83pub struct PodSlotHash {
84 pub slot: Slot,
85 pub hash: Hash,
86}
87
88#[cfg(feature = "bytemuck")]
89#[derive(Default)]
94pub struct PodSlotHashes {
95 data: Vec<u8>,
96 slot_hashes_start: usize,
97 slot_hashes_end: usize,
98}
99
100#[cfg(feature = "bytemuck")]
101impl PodSlotHashes {
102 pub fn fetch() -> Result<Self, clone_solana_program_error::ProgramError> {
104 let sysvar_len = SlotHashes::size_of();
106 let mut data = vec![0; sysvar_len];
107
108 if data.as_ptr().align_offset(8) != 0 {
110 return Err(clone_solana_program_error::ProgramError::InvalidAccountData);
111 }
112
113 crate::get_sysvar(
116 &mut data,
117 &SlotHashes::id(),
118 0,
119 sysvar_len as u64,
120 )?;
121
122 let length = data
128 .get(..U64_SIZE)
129 .and_then(|bytes| bytes.try_into().ok())
130 .map(u64::from_le_bytes)
131 .and_then(|length| length.checked_mul(std::mem::size_of::<PodSlotHash>() as u64))
132 .ok_or(clone_solana_program_error::ProgramError::InvalidAccountData)?;
133
134 let slot_hashes_start = U64_SIZE;
135 let slot_hashes_end = slot_hashes_start.saturating_add(length as usize);
136
137 Ok(Self {
138 data,
139 slot_hashes_start,
140 slot_hashes_end,
141 })
142 }
143
144 pub fn as_slice(&self) -> Result<&[PodSlotHash], clone_solana_program_error::ProgramError> {
147 self.data
148 .get(self.slot_hashes_start..self.slot_hashes_end)
149 .and_then(|data| bytemuck::try_cast_slice(data).ok())
150 .ok_or(clone_solana_program_error::ProgramError::InvalidAccountData)
151 }
152
153 pub fn get(
156 &self,
157 slot: &Slot,
158 ) -> Result<Option<Hash>, clone_solana_program_error::ProgramError> {
159 self.as_slice().map(|pod_hashes| {
160 pod_hashes
161 .binary_search_by(|PodSlotHash { slot: this, .. }| slot.cmp(this))
162 .map(|idx| pod_hashes[idx].hash)
163 .ok()
164 })
165 }
166
167 pub fn position(
170 &self,
171 slot: &Slot,
172 ) -> Result<Option<usize>, clone_solana_program_error::ProgramError> {
173 self.as_slice().map(|pod_hashes| {
174 pod_hashes
175 .binary_search_by(|PodSlotHash { slot: this, .. }| slot.cmp(this))
176 .ok()
177 })
178 }
179}
180
181#[deprecated(since = "2.1.0", note = "Please use `PodSlotHashes` instead")]
183pub struct SlotHashesSysvar;
184
185#[cfg(feature = "bincode")]
186#[allow(deprecated)]
187impl SlotHashesSysvar {
188 #[cfg(feature = "bytemuck")]
189 pub fn get(slot: &Slot) -> Result<Option<Hash>, clone_solana_program_error::ProgramError> {
192 get_pod_slot_hashes().map(|pod_hashes| {
193 pod_hashes
194 .binary_search_by(|PodSlotHash { slot: this, .. }| slot.cmp(this))
195 .map(|idx| pod_hashes[idx].hash)
196 .ok()
197 })
198 }
199
200 #[cfg(feature = "bytemuck")]
201 pub fn position(
204 slot: &Slot,
205 ) -> Result<Option<usize>, clone_solana_program_error::ProgramError> {
206 get_pod_slot_hashes().map(|pod_hashes| {
207 pod_hashes
208 .binary_search_by(|PodSlotHash { slot: this, .. }| slot.cmp(this))
209 .ok()
210 })
211 }
212}
213
214#[cfg(feature = "bytemuck")]
215fn get_pod_slot_hashes() -> Result<Vec<PodSlotHash>, clone_solana_program_error::ProgramError> {
216 let mut pod_hashes = vec![PodSlotHash::default(); clone_solana_slot_hashes::MAX_ENTRIES];
217 {
218 let data = bytemuck::try_cast_slice_mut::<PodSlotHash, u8>(&mut pod_hashes)
219 .map_err(|_| clone_solana_program_error::ProgramError::InvalidAccountData)?;
220
221 if data.as_ptr().align_offset(8) != 0 {
223 return Err(clone_solana_program_error::ProgramError::InvalidAccountData);
224 }
225
226 let offset = 8; let length = (SlotHashes::size_of() as u64).saturating_sub(offset);
228 crate::get_sysvar(data, &SlotHashes::id(), offset, length)?;
229 }
230 Ok(pod_hashes)
231}
232
233#[cfg(test)]
234mod tests {
235 use {
236 super::*, crate::tests::mock_get_sysvar_syscall, clone_solana_hash::Hash,
237 clone_solana_sha256_hasher::hash, clone_solana_slot_hashes::MAX_ENTRIES,
238 serial_test::serial, test_case::test_case,
239 };
240
241 #[test]
242 fn test_size_of() {
243 assert_eq!(
244 SlotHashes::size_of(),
245 bincode::serialized_size(
246 &(0..MAX_ENTRIES)
247 .map(|slot| (slot as Slot, Hash::default()))
248 .collect::<SlotHashes>()
249 )
250 .unwrap() as usize
251 );
252 }
253
254 fn mock_slot_hashes(slot_hashes: &SlotHashes) {
255 let mut data = vec![0; SlotHashes::size_of()];
257 bincode::serialize_into(&mut data[..], slot_hashes).unwrap();
258 mock_get_sysvar_syscall(&data);
259 }
260
261 #[test_case(0)]
262 #[test_case(1)]
263 #[test_case(2)]
264 #[test_case(5)]
265 #[test_case(10)]
266 #[test_case(64)]
267 #[test_case(128)]
268 #[test_case(192)]
269 #[test_case(256)]
270 #[test_case(384)]
271 #[test_case(MAX_ENTRIES)]
272 #[serial]
273 fn test_pod_slot_hashes(num_entries: usize) {
274 let mut slot_hashes = vec![];
275 for i in 0..num_entries {
276 slot_hashes.push((
277 i as u64,
278 hash(&[(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]),
279 ));
280 }
281
282 let check_slot_hashes = SlotHashes::new(&slot_hashes);
283 mock_slot_hashes(&check_slot_hashes);
284
285 let pod_slot_hashes = PodSlotHashes::fetch().unwrap();
286
287 let pod_slot_hashes_slice = pod_slot_hashes.as_slice().unwrap();
290 assert_eq!(pod_slot_hashes_slice.len(), slot_hashes.len());
291
292 for slot in slot_hashes.iter().map(|(slot, _hash)| slot) {
295 assert_eq!(
297 pod_slot_hashes.get(slot).unwrap().as_ref(),
298 check_slot_hashes.get(slot),
299 );
300 assert_eq!(
302 pod_slot_hashes.position(slot).unwrap(),
303 check_slot_hashes.position(slot),
304 );
305 }
306
307 let not_a_slot = num_entries.saturating_add(1) as u64;
309 assert_eq!(
310 pod_slot_hashes.get(¬_a_slot).unwrap().as_ref(),
311 check_slot_hashes.get(¬_a_slot),
312 );
313 assert_eq!(pod_slot_hashes.get(¬_a_slot).unwrap(), None);
314 assert_eq!(
315 pod_slot_hashes.position(¬_a_slot).unwrap(),
316 check_slot_hashes.position(¬_a_slot),
317 );
318 assert_eq!(pod_slot_hashes.position(¬_a_slot).unwrap(), None);
319
320 let not_a_slot = num_entries.saturating_add(2) as u64;
321 assert_eq!(
322 pod_slot_hashes.get(¬_a_slot).unwrap().as_ref(),
323 check_slot_hashes.get(¬_a_slot),
324 );
325 assert_eq!(pod_slot_hashes.get(¬_a_slot).unwrap(), None);
326 assert_eq!(
327 pod_slot_hashes.position(¬_a_slot).unwrap(),
328 check_slot_hashes.position(¬_a_slot),
329 );
330 assert_eq!(pod_slot_hashes.position(¬_a_slot).unwrap(), None);
331 }
332
333 #[allow(deprecated)]
334 #[serial]
335 #[test]
336 fn test_slot_hashes_sysvar() {
337 let mut slot_hashes = vec![];
338 for i in 0..MAX_ENTRIES {
339 slot_hashes.push((
340 i as u64,
341 hash(&[(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]),
342 ));
343 }
344
345 let check_slot_hashes = SlotHashes::new(&slot_hashes);
346 mock_get_sysvar_syscall(&bincode::serialize(&check_slot_hashes).unwrap());
347
348 assert_eq!(
350 SlotHashesSysvar::get(&0).unwrap().as_ref(),
351 check_slot_hashes.get(&0),
352 );
353 assert_eq!(
354 SlotHashesSysvar::get(&256).unwrap().as_ref(),
355 check_slot_hashes.get(&256),
356 );
357 assert_eq!(
358 SlotHashesSysvar::get(&511).unwrap().as_ref(),
359 check_slot_hashes.get(&511),
360 );
361 assert_eq!(
363 SlotHashesSysvar::get(&600).unwrap().as_ref(),
364 check_slot_hashes.get(&600),
365 );
366
367 assert_eq!(
369 SlotHashesSysvar::position(&0).unwrap(),
370 check_slot_hashes.position(&0),
371 );
372 assert_eq!(
373 SlotHashesSysvar::position(&256).unwrap(),
374 check_slot_hashes.position(&256),
375 );
376 assert_eq!(
377 SlotHashesSysvar::position(&511).unwrap(),
378 check_slot_hashes.position(&511),
379 );
380 assert_eq!(
382 SlotHashesSysvar::position(&600).unwrap(),
383 check_slot_hashes.position(&600),
384 );
385 }
386}