1#[allow(deprecated)]
2use solana_sysvar::{fees::Fees, recent_blockhashes::RecentBlockhashes};
3use {
4 crate::invoke_context::InvokeContext,
5 serde::de::DeserializeOwned,
6 solana_clock::Clock,
7 solana_epoch_rewards::EpochRewards,
8 solana_epoch_schedule::EpochSchedule,
9 solana_instruction::error::InstructionError,
10 solana_last_restart_slot::LastRestartSlot,
11 solana_pubkey::Pubkey,
12 solana_rent::Rent,
13 solana_sdk_ids::sysvar,
14 solana_slot_hashes::SlotHashes,
15 solana_stake_interface::stake_history::StakeHistory,
16 solana_svm_type_overrides::sync::Arc,
17 solana_sysvar::SysvarSerialize,
18 solana_sysvar_id::SysvarId,
19 solana_transaction_context::{IndexOfAccount, InstructionContext},
20};
21
22#[cfg(feature = "frozen-abi")]
23impl ::solana_frozen_abi::abi_example::AbiExample for SysvarCache {
24 fn example() -> Self {
25 SysvarCache::default()
27 }
28}
29
30#[derive(Default, Clone, Debug)]
31pub struct SysvarCache {
32 clock: Option<Vec<u8>>,
34 epoch_schedule: Option<Vec<u8>>,
35 epoch_rewards: Option<Vec<u8>>,
36 rent: Option<Vec<u8>>,
37 slot_hashes: Option<Vec<u8>>,
38 stake_history: Option<Vec<u8>>,
39 last_restart_slot: Option<Vec<u8>>,
40
41 slot_hashes_obj: Option<Arc<SlotHashes>>,
45 stake_history_obj: Option<Arc<StakeHistory>>,
46
47 #[allow(deprecated)]
49 fees: Option<Fees>,
50 #[allow(deprecated)]
51 recent_blockhashes: Option<RecentBlockhashes>,
52}
53
54const FEES_ID: Pubkey = Pubkey::from_str_const("SysvarFees111111111111111111111111111111111");
57const RECENT_BLOCKHASHES_ID: Pubkey =
58 Pubkey::from_str_const("SysvarRecentB1ockHashes11111111111111111111");
59
60impl SysvarCache {
61 #[allow(deprecated)]
63 pub fn set_sysvar_for_tests<T: SysvarSerialize + SysvarId>(&mut self, sysvar: &T) {
64 let data = bincode::serialize(sysvar).expect("Failed to serialize sysvar.");
65 let sysvar_id = T::id();
66 match sysvar_id {
67 sysvar::clock::ID => {
68 self.clock = Some(data);
69 }
70 sysvar::epoch_rewards::ID => {
71 self.epoch_rewards = Some(data);
72 }
73 sysvar::epoch_schedule::ID => {
74 self.epoch_schedule = Some(data);
75 }
76 FEES_ID => {
77 let fees: Fees =
78 bincode::deserialize(&data).expect("Failed to deserialize Fees sysvar.");
79 self.fees = Some(fees);
80 }
81 sysvar::last_restart_slot::ID => {
82 self.last_restart_slot = Some(data);
83 }
84 RECENT_BLOCKHASHES_ID => {
85 let recent_blockhashes: RecentBlockhashes = bincode::deserialize(&data)
86 .expect("Failed to deserialize RecentBlockhashes sysvar.");
87 self.recent_blockhashes = Some(recent_blockhashes);
88 }
89 sysvar::rent::ID => {
90 self.rent = Some(data);
91 }
92 sysvar::slot_hashes::ID => {
93 let slot_hashes: SlotHashes =
94 bincode::deserialize(&data).expect("Failed to deserialize SlotHashes sysvar.");
95 self.slot_hashes = Some(data);
96 self.slot_hashes_obj = Some(Arc::new(slot_hashes));
97 }
98 sysvar::stake_history::ID => {
99 let stake_history: StakeHistory = bincode::deserialize(&data)
100 .expect("Failed to deserialize StakeHistory sysvar.");
101 self.stake_history = Some(data);
102 self.stake_history_obj = Some(Arc::new(stake_history));
103 }
104 _ => panic!("Unrecognized Sysvar ID: {sysvar_id}"),
105 }
106 }
107
108 pub fn sysvar_id_to_buffer(&self, sysvar_id: &Pubkey) -> &Option<Vec<u8>> {
110 if Clock::check_id(sysvar_id) {
111 &self.clock
112 } else if EpochSchedule::check_id(sysvar_id) {
113 &self.epoch_schedule
114 } else if EpochRewards::check_id(sysvar_id) {
115 &self.epoch_rewards
116 } else if Rent::check_id(sysvar_id) {
117 &self.rent
118 } else if SlotHashes::check_id(sysvar_id) {
119 &self.slot_hashes
120 } else if StakeHistory::check_id(sysvar_id) {
121 &self.stake_history
122 } else if LastRestartSlot::check_id(sysvar_id) {
123 &self.last_restart_slot
124 } else {
125 &None
126 }
127 }
128
129 fn get_sysvar_obj<T: DeserializeOwned>(
132 &self,
133 sysvar_id: &Pubkey,
134 ) -> Result<Arc<T>, InstructionError> {
135 if let Some(ref sysvar_buf) = self.sysvar_id_to_buffer(sysvar_id) {
136 bincode::deserialize(sysvar_buf)
137 .map(Arc::new)
138 .map_err(|_| InstructionError::UnsupportedSysvar)
139 } else {
140 Err(InstructionError::UnsupportedSysvar)
141 }
142 }
143
144 pub fn get_clock(&self) -> Result<Arc<Clock>, InstructionError> {
145 self.get_sysvar_obj(&Clock::id())
146 }
147
148 pub fn get_epoch_schedule(&self) -> Result<Arc<EpochSchedule>, InstructionError> {
149 self.get_sysvar_obj(&EpochSchedule::id())
150 }
151
152 pub fn get_epoch_rewards(&self) -> Result<Arc<EpochRewards>, InstructionError> {
153 self.get_sysvar_obj(&EpochRewards::id())
154 }
155
156 pub fn get_rent(&self) -> Result<Arc<Rent>, InstructionError> {
157 self.get_sysvar_obj(&Rent::id())
158 }
159
160 pub fn get_last_restart_slot(&self) -> Result<Arc<LastRestartSlot>, InstructionError> {
161 self.get_sysvar_obj(&LastRestartSlot::id())
162 }
163
164 pub fn get_stake_history(&self) -> Result<Arc<StakeHistory>, InstructionError> {
165 self.stake_history_obj
166 .clone()
167 .ok_or(InstructionError::UnsupportedSysvar)
168 }
169
170 pub fn get_slot_hashes(&self) -> Result<Arc<SlotHashes>, InstructionError> {
171 self.slot_hashes_obj
172 .clone()
173 .ok_or(InstructionError::UnsupportedSysvar)
174 }
175
176 #[deprecated]
177 #[allow(deprecated)]
178 pub fn get_fees(&self) -> Result<Arc<Fees>, InstructionError> {
179 self.fees
180 .clone()
181 .ok_or(InstructionError::UnsupportedSysvar)
182 .map(Arc::new)
183 }
184
185 #[deprecated]
186 #[allow(deprecated)]
187 pub fn get_recent_blockhashes(&self) -> Result<Arc<RecentBlockhashes>, InstructionError> {
188 self.recent_blockhashes
189 .clone()
190 .ok_or(InstructionError::UnsupportedSysvar)
191 .map(Arc::new)
192 }
193
194 pub fn fill_missing_entries<F: FnMut(&Pubkey, &mut dyn FnMut(&[u8]))>(
195 &mut self,
196 mut get_account_data: F,
197 ) {
198 if self.clock.is_none() {
199 get_account_data(&Clock::id(), &mut |data: &[u8]| {
200 if bincode::deserialize::<Clock>(data).is_ok() {
201 self.clock = Some(data.to_vec());
202 }
203 });
204 }
205
206 if self.epoch_schedule.is_none() {
207 get_account_data(&EpochSchedule::id(), &mut |data: &[u8]| {
208 if bincode::deserialize::<EpochSchedule>(data).is_ok() {
209 self.epoch_schedule = Some(data.to_vec());
210 }
211 });
212 }
213
214 if self.epoch_rewards.is_none() {
215 get_account_data(&EpochRewards::id(), &mut |data: &[u8]| {
216 if bincode::deserialize::<EpochRewards>(data).is_ok() {
217 self.epoch_rewards = Some(data.to_vec());
218 }
219 });
220 }
221
222 if self.rent.is_none() {
223 get_account_data(&Rent::id(), &mut |data: &[u8]| {
224 if bincode::deserialize::<Rent>(data).is_ok() {
225 self.rent = Some(data.to_vec());
226 }
227 });
228 }
229
230 if self.slot_hashes.is_none() {
231 get_account_data(&SlotHashes::id(), &mut |data: &[u8]| {
232 if let Ok(obj) = bincode::deserialize::<SlotHashes>(data) {
233 self.slot_hashes = Some(data.to_vec());
234 self.slot_hashes_obj = Some(Arc::new(obj));
235 }
236 });
237 }
238
239 if self.stake_history.is_none() {
240 get_account_data(&StakeHistory::id(), &mut |data: &[u8]| {
241 if let Ok(obj) = bincode::deserialize::<StakeHistory>(data) {
242 self.stake_history = Some(data.to_vec());
243 self.stake_history_obj = Some(Arc::new(obj));
244 }
245 });
246 }
247
248 if self.last_restart_slot.is_none() {
249 get_account_data(&LastRestartSlot::id(), &mut |data: &[u8]| {
250 if bincode::deserialize::<LastRestartSlot>(data).is_ok() {
251 self.last_restart_slot = Some(data.to_vec());
252 }
253 });
254 }
255
256 #[allow(deprecated)]
257 if self.fees.is_none() {
258 get_account_data(&Fees::id(), &mut |data: &[u8]| {
259 if let Ok(fees) = bincode::deserialize(data) {
260 self.fees = Some(fees);
261 }
262 });
263 }
264
265 #[allow(deprecated)]
266 if self.recent_blockhashes.is_none() {
267 get_account_data(&RecentBlockhashes::id(), &mut |data: &[u8]| {
268 if let Ok(recent_blockhashes) = bincode::deserialize(data) {
269 self.recent_blockhashes = Some(recent_blockhashes);
270 }
271 });
272 }
273 }
274
275 pub fn reset(&mut self) {
276 *self = Self::default();
277 }
278}
279
280pub mod get_sysvar_with_account_check {
285 use super::*;
286
287 fn check_sysvar_account<S: SysvarId>(
288 instruction_context: &InstructionContext,
289 instruction_account_index: IndexOfAccount,
290 ) -> Result<(), InstructionError> {
291 if !S::check_id(
292 instruction_context.get_key_of_instruction_account(instruction_account_index)?,
293 ) {
294 return Err(InstructionError::InvalidArgument);
295 }
296 Ok(())
297 }
298
299 pub fn clock(
300 invoke_context: &InvokeContext,
301 instruction_context: &InstructionContext,
302 instruction_account_index: IndexOfAccount,
303 ) -> Result<Arc<Clock>, InstructionError> {
304 check_sysvar_account::<Clock>(instruction_context, instruction_account_index)?;
305 invoke_context.get_sysvar_cache().get_clock()
306 }
307
308 pub fn rent(
309 invoke_context: &InvokeContext,
310 instruction_context: &InstructionContext,
311 instruction_account_index: IndexOfAccount,
312 ) -> Result<Arc<Rent>, InstructionError> {
313 check_sysvar_account::<Rent>(instruction_context, instruction_account_index)?;
314 invoke_context.get_sysvar_cache().get_rent()
315 }
316
317 pub fn slot_hashes(
318 invoke_context: &InvokeContext,
319 instruction_context: &InstructionContext,
320 instruction_account_index: IndexOfAccount,
321 ) -> Result<Arc<SlotHashes>, InstructionError> {
322 check_sysvar_account::<SlotHashes>(instruction_context, instruction_account_index)?;
323 invoke_context.get_sysvar_cache().get_slot_hashes()
324 }
325
326 #[allow(deprecated)]
327 pub fn recent_blockhashes(
328 invoke_context: &InvokeContext,
329 instruction_context: &InstructionContext,
330 instruction_account_index: IndexOfAccount,
331 ) -> Result<Arc<RecentBlockhashes>, InstructionError> {
332 check_sysvar_account::<RecentBlockhashes>(instruction_context, instruction_account_index)?;
333 invoke_context.get_sysvar_cache().get_recent_blockhashes()
334 }
335
336 pub fn stake_history(
337 invoke_context: &InvokeContext,
338 instruction_context: &InstructionContext,
339 instruction_account_index: IndexOfAccount,
340 ) -> Result<Arc<StakeHistory>, InstructionError> {
341 check_sysvar_account::<StakeHistory>(instruction_context, instruction_account_index)?;
342 invoke_context.get_sysvar_cache().get_stake_history()
343 }
344
345 pub fn last_restart_slot(
346 invoke_context: &InvokeContext,
347 instruction_context: &InstructionContext,
348 instruction_account_index: IndexOfAccount,
349 ) -> Result<Arc<LastRestartSlot>, InstructionError> {
350 check_sysvar_account::<LastRestartSlot>(instruction_context, instruction_account_index)?;
351 invoke_context.get_sysvar_cache().get_last_restart_slot()
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use {super::*, test_case::test_case};
358
359 #[test_case(Clock::default(); "clock")]
367 #[test_case(EpochSchedule::default(); "epoch_schedule")]
368 #[test_case(EpochRewards::default(); "epoch_rewards")]
369 #[test_case(Rent::default(); "rent")]
370 #[test_case(SlotHashes::default(); "slot_hashes")]
371 #[test_case(StakeHistory::default(); "stake_history")]
372 #[test_case(LastRestartSlot::default(); "last_restart_slot")]
373 fn test_sysvar_cache_preserves_bytes<T: SysvarSerialize>(_: T) {
374 let id = T::id();
375 let size = T::size_of().saturating_mul(2);
376 let in_buf = vec![0; size];
377
378 let mut sysvar_cache = SysvarCache::default();
379 sysvar_cache.fill_missing_entries(|pubkey, callback| {
380 if *pubkey == id {
381 callback(&in_buf)
382 }
383 });
384 let sysvar_cache = sysvar_cache;
385
386 let out_buf = sysvar_cache.sysvar_id_to_buffer(&id).clone().unwrap();
387
388 assert_eq!(out_buf, in_buf);
389 }
390}