rialo_s_program_runtime/
sysvar_cache.rs1use rialo_s_clock::Clock;
6use rialo_s_epoch_schedule::EpochSchedule;
7use rialo_s_instruction::error::InstructionError;
8use rialo_s_pubkey::Pubkey;
9use rialo_s_rent::Rent;
10#[cfg(any(test, feature = "dev-context-only-utils"))]
11use rialo_s_sdk_ids::sysvar;
12#[allow(deprecated)]
13use rialo_s_sysvar::recent_blockhashes::RecentBlockhashes;
14use rialo_s_sysvar::Sysvar;
15use rialo_s_sysvar_id::SysvarId;
16use rialo_s_transaction_context::{IndexOfAccount, InstructionContext, TransactionContext};
17use rialo_s_type_overrides::sync::Arc;
18use serde::de::DeserializeOwned;
19
20use crate::invoke_context::InvokeContext;
21
22#[cfg(feature = "frozen-abi")]
23impl ::rialo_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 rent: Option<Vec<u8>>,
36
37 #[allow(deprecated)]
38 recent_blockhashes: Option<RecentBlockhashes>,
39}
40
41#[cfg(any(test, feature = "dev-context-only-utils"))]
42const RECENT_BLOCKHASHES_ID: Pubkey =
43 Pubkey::from_str_const("SysvarRecentB1ockHashes11111111111111111111");
44
45impl SysvarCache {
46 #[allow(deprecated)]
48 #[cfg(any(test, feature = "dev-context-only-utils"))]
49 pub fn set_sysvar_for_tests<T: Sysvar + SysvarId>(&mut self, sysvar: &T) {
50 let data = bincode::serialize(sysvar).expect("Failed to serialize sysvar.");
51 let sysvar_id = T::id();
52 match sysvar_id {
53 sysvar::clock::ID => {
54 self.clock = Some(data);
55 }
56 sysvar::epoch_schedule::ID => {
57 self.epoch_schedule = Some(data);
58 }
59 RECENT_BLOCKHASHES_ID => {
60 let recent_blockhashes: RecentBlockhashes = bincode::deserialize(&data)
61 .expect("Failed to deserialize RecentBlockhashes sysvar.");
62 self.recent_blockhashes = Some(recent_blockhashes);
63 }
64 sysvar::rent::ID => {
65 self.rent = Some(data);
66 }
67 _ => panic!("Unrecognized Sysvar ID: {sysvar_id}"),
68 }
69 }
70
71 pub fn sysvar_id_to_buffer(&self, sysvar_id: &Pubkey) -> &Option<Vec<u8>> {
73 if Clock::check_id(sysvar_id) {
74 &self.clock
75 } else if EpochSchedule::check_id(sysvar_id) {
76 &self.epoch_schedule
77 } else if Rent::check_id(sysvar_id) {
78 &self.rent
79 } else {
80 &None
81 }
82 }
83
84 fn get_sysvar_obj<T: DeserializeOwned>(
87 &self,
88 sysvar_id: &Pubkey,
89 ) -> Result<Arc<T>, InstructionError> {
90 if let Some(ref sysvar_buf) = self.sysvar_id_to_buffer(sysvar_id) {
91 bincode::deserialize(sysvar_buf)
92 .map(Arc::new)
93 .map_err(|_| InstructionError::UnsupportedSysvar)
94 } else {
95 Err(InstructionError::UnsupportedSysvar)
96 }
97 }
98
99 pub fn get_clock(&self) -> Result<Arc<Clock>, InstructionError> {
100 self.get_sysvar_obj(&Clock::id())
101 }
102
103 pub fn get_epoch_schedule(&self) -> Result<Arc<EpochSchedule>, InstructionError> {
104 self.get_sysvar_obj(&EpochSchedule::id())
105 }
106
107 pub fn get_rent(&self) -> Result<Arc<Rent>, InstructionError> {
108 self.get_sysvar_obj(&Rent::id())
109 }
110
111 #[deprecated]
112 #[allow(deprecated)]
113 pub fn get_recent_blockhashes(&self) -> Result<Arc<RecentBlockhashes>, InstructionError> {
114 self.recent_blockhashes
115 .clone()
116 .ok_or(InstructionError::UnsupportedSysvar)
117 .map(Arc::new)
118 }
119
120 pub fn fill_missing_entries<F: FnMut(&Pubkey, &mut dyn FnMut(&[u8]))>(
121 &mut self,
122 mut get_account_data: F,
123 ) {
124 if self.clock.is_none() {
125 get_account_data(&Clock::id(), &mut |data: &[u8]| {
126 if bincode::deserialize::<Clock>(data).is_ok() {
127 self.clock = Some(data.to_vec());
128 }
129 });
130 }
131
132 if self.epoch_schedule.is_none() {
133 get_account_data(&EpochSchedule::id(), &mut |data: &[u8]| {
134 if bincode::deserialize::<EpochSchedule>(data).is_ok() {
135 self.epoch_schedule = Some(data.to_vec());
136 }
137 });
138 }
139
140 if self.rent.is_none() {
141 get_account_data(&Rent::id(), &mut |data: &[u8]| {
142 if bincode::deserialize::<Rent>(data).is_ok() {
143 self.rent = Some(data.to_vec());
144 }
145 });
146 }
147
148 #[allow(deprecated)]
149 if self.recent_blockhashes.is_none() {
150 get_account_data(&RecentBlockhashes::id(), &mut |data: &[u8]| {
151 if let Ok(recent_blockhashes) = bincode::deserialize(data) {
152 self.recent_blockhashes = Some(recent_blockhashes);
153 }
154 });
155 }
156 }
157
158 pub fn reset(&mut self) {
159 *self = Self::default();
160 }
161}
162
163pub mod get_sysvar_with_account_check {
169 use super::*;
170
171 fn check_sysvar_account<S: Sysvar>(
172 transaction_context: &TransactionContext,
173 instruction_context: &InstructionContext,
174 instruction_account_index: IndexOfAccount,
175 ) -> Result<(), InstructionError> {
176 let index_in_transaction = instruction_context
177 .get_index_of_instruction_account_in_transaction(instruction_account_index)?;
178 if !S::check_id(transaction_context.get_key_of_account_at_index(index_in_transaction)?) {
179 return Err(InstructionError::InvalidArgument);
180 }
181 Ok(())
182 }
183
184 pub fn clock(
185 invoke_context: &InvokeContext<'_>,
186 instruction_context: &InstructionContext,
187 instruction_account_index: IndexOfAccount,
188 ) -> Result<Arc<Clock>, InstructionError> {
189 check_sysvar_account::<Clock>(
190 invoke_context.transaction_context,
191 instruction_context,
192 instruction_account_index,
193 )?;
194 invoke_context.get_sysvar_cache().get_clock()
195 }
196
197 pub fn rent(
198 invoke_context: &InvokeContext<'_>,
199 instruction_context: &InstructionContext,
200 instruction_account_index: IndexOfAccount,
201 ) -> Result<Arc<Rent>, InstructionError> {
202 check_sysvar_account::<Rent>(
203 invoke_context.transaction_context,
204 instruction_context,
205 instruction_account_index,
206 )?;
207 invoke_context.get_sysvar_cache().get_rent()
208 }
209
210 #[allow(deprecated)]
211 pub fn recent_blockhashes(
212 invoke_context: &InvokeContext<'_>,
213 instruction_context: &InstructionContext,
214 instruction_account_index: IndexOfAccount,
215 ) -> Result<Arc<RecentBlockhashes>, InstructionError> {
216 check_sysvar_account::<RecentBlockhashes>(
217 invoke_context.transaction_context,
218 instruction_context,
219 instruction_account_index,
220 )?;
221 invoke_context.get_sysvar_cache().get_recent_blockhashes()
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use test_case::test_case;
228
229 use super::*;
230
231 #[test_case(Clock::default(); "clock")]
239 #[test_case(EpochSchedule::default(); "epoch_schedule")]
240 #[test_case(Rent::default(); "rent")]
241 fn test_sysvar_cache_preserves_bytes<T: Sysvar>(_: T) {
242 let id = T::id();
243 let size = T::size_of().saturating_mul(2);
244 let in_buf = vec![0; size];
245
246 let mut sysvar_cache = SysvarCache::default();
247 sysvar_cache.fill_missing_entries(|pubkey, callback| {
248 if *pubkey == id {
249 callback(&in_buf)
250 }
251 });
252 let sysvar_cache = sysvar_cache;
253
254 let out_buf = sysvar_cache.sysvar_id_to_buffer(&id).clone().unwrap();
255
256 assert_eq!(out_buf, in_buf);
257 }
258}