solana_sysvar/
slot_hashes.rs1#[cfg(feature = "bytemuck")]
47use bytemuck_derive::{Pod, Zeroable};
48use {solana_clock::Slot, solana_hash::Hash};
49
50#[cfg(feature = "bytemuck")]
51const U64_SIZE: usize = std::mem::size_of::<u64>();
52
53pub use {
54 solana_sdk_ids::sysvar::slot_hashes::{check_id, id, ID},
55 solana_slot_hashes::{SlotHashes, SIZE},
56 solana_sysvar_id::SysvarId,
57};
58
59#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
61#[derive(Copy, Clone, Default)]
62#[repr(C)]
63pub struct PodSlotHash {
64 pub slot: Slot,
65 pub hash: Hash,
66}
67
68#[cfg(feature = "bytemuck")]
69#[derive(Default)]
74pub struct PodSlotHashes {
75 data: Vec<u8>,
76 slot_hashes_start: usize,
77 slot_hashes_end: usize,
78}
79
80#[cfg(feature = "bytemuck")]
81impl PodSlotHashes {
82 pub fn fetch() -> Result<Self, solana_program_error::ProgramError> {
84 let sysvar_len = SIZE;
86 let mut data = vec![0; sysvar_len];
87
88 if data.as_ptr().align_offset(8) != 0 {
90 return Err(solana_program_error::ProgramError::InvalidAccountData);
91 }
92
93 crate::get_sysvar(
96 &mut data,
97 &SlotHashes::id(),
98 0,
99 sysvar_len as u64,
100 )?;
101
102 Self::from_bytes(data)
103 }
104
105 fn from_bytes(data: Vec<u8>) -> Result<Self, solana_program_error::ProgramError> {
106 let length = data
112 .get(..U64_SIZE)
113 .and_then(|bytes| bytes.try_into().ok())
114 .map(u64::from_le_bytes)
115 .and_then(|length| length.checked_mul(std::mem::size_of::<PodSlotHash>() as u64))
116 .ok_or(solana_program_error::ProgramError::InvalidAccountData)?;
117
118 let slot_hashes_start = U64_SIZE;
119 let slot_hashes_end = slot_hashes_start.saturating_add(length as usize);
120
121 Ok(Self {
122 data,
123 slot_hashes_start,
124 slot_hashes_end,
125 })
126 }
127
128 pub fn as_slice(&self) -> Result<&[PodSlotHash], solana_program_error::ProgramError> {
131 self.data
132 .get(self.slot_hashes_start..self.slot_hashes_end)
133 .and_then(|data| bytemuck::try_cast_slice(data).ok())
134 .ok_or(solana_program_error::ProgramError::InvalidAccountData)
135 }
136
137 pub fn get(&self, slot: &Slot) -> Result<Option<Hash>, solana_program_error::ProgramError> {
140 self.as_slice().map(|pod_hashes| {
141 pod_hashes
142 .binary_search_by(|PodSlotHash { slot: this, .. }| slot.cmp(this))
143 .map(|idx| pod_hashes[idx].hash)
144 .ok()
145 })
146 }
147
148 pub fn position(
151 &self,
152 slot: &Slot,
153 ) -> Result<Option<usize>, solana_program_error::ProgramError> {
154 self.as_slice().map(|pod_hashes| {
155 pod_hashes
156 .binary_search_by(|PodSlotHash { slot: this, .. }| slot.cmp(this))
157 .ok()
158 })
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use {
165 super::*,
166 solana_sha256_hasher::hash,
167 solana_slot_hashes::{SlotHash, MAX_ENTRIES},
168 test_case::test_case,
169 };
170
171 #[test_case(0)]
172 #[test_case(1)]
173 #[test_case(2)]
174 #[test_case(5)]
175 #[test_case(10)]
176 #[test_case(64)]
177 #[test_case(128)]
178 #[test_case(192)]
179 #[test_case(256)]
180 #[test_case(384)]
181 #[test_case(MAX_ENTRIES)]
182 fn test_pod_slot_hashes(num_entries: usize) {
183 let mut slot_hashes = vec![];
184 for i in 0..num_entries {
185 slot_hashes.push(SlotHash::new(
186 i as u64,
187 hash(&[(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]),
188 ));
189 }
190
191 let check_slot_hashes = SlotHashes::new(&slot_hashes);
192 let pod_slot_hashes =
193 PodSlotHashes::from_bytes(wincode::serialize(&check_slot_hashes).unwrap()).unwrap();
194
195 let pod_slot_hashes_slice = pod_slot_hashes.as_slice().unwrap();
198 assert_eq!(pod_slot_hashes_slice.len(), slot_hashes.len());
199
200 for slot in slot_hashes.iter().map(|entry| &entry.slot) {
203 assert_eq!(
205 pod_slot_hashes.get(slot).unwrap().as_ref(),
206 check_slot_hashes.get(slot),
207 );
208 assert_eq!(
210 pod_slot_hashes.position(slot).unwrap(),
211 check_slot_hashes.position(slot),
212 );
213 }
214
215 let not_a_slot = num_entries.saturating_add(1) as u64;
217 assert_eq!(
218 pod_slot_hashes.get(¬_a_slot).unwrap().as_ref(),
219 check_slot_hashes.get(¬_a_slot),
220 );
221 assert_eq!(pod_slot_hashes.get(¬_a_slot).unwrap(), None);
222 assert_eq!(
223 pod_slot_hashes.position(¬_a_slot).unwrap(),
224 check_slot_hashes.position(¬_a_slot),
225 );
226 assert_eq!(pod_slot_hashes.position(¬_a_slot).unwrap(), None);
227
228 let not_a_slot = num_entries.saturating_add(2) as u64;
229 assert_eq!(
230 pod_slot_hashes.get(¬_a_slot).unwrap().as_ref(),
231 check_slot_hashes.get(¬_a_slot),
232 );
233 assert_eq!(pod_slot_hashes.get(¬_a_slot).unwrap(), None);
234 assert_eq!(
235 pod_slot_hashes.position(¬_a_slot).unwrap(),
236 check_slot_hashes.position(¬_a_slot),
237 );
238 assert_eq!(pod_slot_hashes.position(¬_a_slot).unwrap(), None);
239 }
240}