miraland_program_runtime/
sysvar_cache.rs

1#[allow(deprecated)]
2use miraland_sdk::sysvar::{
3    fees::Fees, last_restart_slot::LastRestartSlot, recent_blockhashes::RecentBlockhashes,
4};
5use {
6    crate::invoke_context::InvokeContext,
7    miraland_sdk::{
8        instruction::InstructionError,
9        pubkey::Pubkey,
10        sysvar::{
11            clock::Clock, epoch_rewards::EpochRewards, epoch_schedule::EpochSchedule, rent::Rent,
12            slot_hashes::SlotHashes, stake_history::StakeHistory, Sysvar, SysvarId,
13        },
14        transaction_context::{IndexOfAccount, InstructionContext, TransactionContext},
15    },
16    std::sync::Arc,
17};
18
19#[cfg(RUSTC_WITH_SPECIALIZATION)]
20impl ::miraland_frozen_abi::abi_example::AbiExample for SysvarCache {
21    fn example() -> Self {
22        // SysvarCache is not Serialize so just rely on Default.
23        SysvarCache::default()
24    }
25}
26
27#[derive(Default, Clone, Debug)]
28pub struct SysvarCache {
29    clock: Option<Arc<Clock>>,
30    epoch_schedule: Option<Arc<EpochSchedule>>,
31    epoch_rewards: Option<Arc<EpochRewards>>,
32    #[allow(deprecated)]
33    fees: Option<Arc<Fees>>,
34    rent: Option<Arc<Rent>>,
35    slot_hashes: Option<Arc<SlotHashes>>,
36    #[allow(deprecated)]
37    recent_blockhashes: Option<Arc<RecentBlockhashes>>,
38    stake_history: Option<Arc<StakeHistory>>,
39    last_restart_slot: Option<Arc<LastRestartSlot>>,
40}
41
42impl SysvarCache {
43    pub fn get_clock(&self) -> Result<Arc<Clock>, InstructionError> {
44        self.clock
45            .clone()
46            .ok_or(InstructionError::UnsupportedSysvar)
47    }
48
49    pub fn set_clock(&mut self, clock: Clock) {
50        self.clock = Some(Arc::new(clock));
51    }
52
53    pub fn get_epoch_schedule(&self) -> Result<Arc<EpochSchedule>, InstructionError> {
54        self.epoch_schedule
55            .clone()
56            .ok_or(InstructionError::UnsupportedSysvar)
57    }
58
59    pub fn set_epoch_schedule(&mut self, epoch_schedule: EpochSchedule) {
60        self.epoch_schedule = Some(Arc::new(epoch_schedule));
61    }
62
63    pub fn get_epoch_rewards(&self) -> Result<Arc<EpochRewards>, InstructionError> {
64        self.epoch_rewards
65            .clone()
66            .ok_or(InstructionError::UnsupportedSysvar)
67    }
68
69    pub fn set_epoch_rewards(&mut self, epoch_rewards: EpochRewards) {
70        self.epoch_rewards = Some(Arc::new(epoch_rewards));
71    }
72
73    #[deprecated]
74    #[allow(deprecated)]
75    pub fn get_fees(&self) -> Result<Arc<Fees>, InstructionError> {
76        self.fees.clone().ok_or(InstructionError::UnsupportedSysvar)
77    }
78
79    #[deprecated]
80    #[allow(deprecated)]
81    pub fn set_fees(&mut self, fees: Fees) {
82        self.fees = Some(Arc::new(fees));
83    }
84
85    pub fn get_rent(&self) -> Result<Arc<Rent>, InstructionError> {
86        self.rent.clone().ok_or(InstructionError::UnsupportedSysvar)
87    }
88
89    pub fn set_rent(&mut self, rent: Rent) {
90        self.rent = Some(Arc::new(rent));
91    }
92
93    pub fn get_last_restart_slot(&self) -> Result<Arc<LastRestartSlot>, InstructionError> {
94        self.last_restart_slot
95            .clone()
96            .ok_or(InstructionError::UnsupportedSysvar)
97    }
98
99    pub fn set_last_restart_slot(&mut self, last_restart_slot: LastRestartSlot) {
100        self.last_restart_slot = Some(Arc::new(last_restart_slot));
101    }
102
103    pub fn get_slot_hashes(&self) -> Result<Arc<SlotHashes>, InstructionError> {
104        self.slot_hashes
105            .clone()
106            .ok_or(InstructionError::UnsupportedSysvar)
107    }
108
109    pub fn set_slot_hashes(&mut self, slot_hashes: SlotHashes) {
110        self.slot_hashes = Some(Arc::new(slot_hashes));
111    }
112
113    #[deprecated]
114    #[allow(deprecated)]
115    pub fn get_recent_blockhashes(&self) -> Result<Arc<RecentBlockhashes>, InstructionError> {
116        self.recent_blockhashes
117            .clone()
118            .ok_or(InstructionError::UnsupportedSysvar)
119    }
120
121    #[deprecated]
122    #[allow(deprecated)]
123    pub fn set_recent_blockhashes(&mut self, recent_blockhashes: RecentBlockhashes) {
124        self.recent_blockhashes = Some(Arc::new(recent_blockhashes));
125    }
126
127    pub fn get_stake_history(&self) -> Result<Arc<StakeHistory>, InstructionError> {
128        self.stake_history
129            .clone()
130            .ok_or(InstructionError::UnsupportedSysvar)
131    }
132
133    pub fn set_stake_history(&mut self, stake_history: StakeHistory) {
134        self.stake_history = Some(Arc::new(stake_history));
135    }
136
137    pub fn fill_missing_entries<F: FnMut(&Pubkey, &mut dyn FnMut(&[u8]))>(
138        &mut self,
139        mut get_account_data: F,
140    ) {
141        if self.clock.is_none() {
142            get_account_data(&Clock::id(), &mut |data: &[u8]| {
143                if let Ok(clock) = bincode::deserialize(data) {
144                    self.set_clock(clock);
145                }
146            });
147        }
148        if self.epoch_schedule.is_none() {
149            get_account_data(&EpochSchedule::id(), &mut |data: &[u8]| {
150                if let Ok(epoch_schedule) = bincode::deserialize(data) {
151                    self.set_epoch_schedule(epoch_schedule);
152                }
153            });
154        }
155
156        if self.epoch_rewards.is_none() {
157            get_account_data(&EpochRewards::id(), &mut |data: &[u8]| {
158                if let Ok(epoch_rewards) = bincode::deserialize(data) {
159                    self.set_epoch_rewards(epoch_rewards);
160                }
161            });
162        }
163
164        #[allow(deprecated)]
165        if self.fees.is_none() {
166            get_account_data(&Fees::id(), &mut |data: &[u8]| {
167                if let Ok(fees) = bincode::deserialize(data) {
168                    self.set_fees(fees);
169                }
170            });
171        }
172        if self.rent.is_none() {
173            get_account_data(&Rent::id(), &mut |data: &[u8]| {
174                if let Ok(rent) = bincode::deserialize(data) {
175                    self.set_rent(rent);
176                }
177            });
178        }
179        if self.slot_hashes.is_none() {
180            get_account_data(&SlotHashes::id(), &mut |data: &[u8]| {
181                if let Ok(slot_hashes) = bincode::deserialize(data) {
182                    self.set_slot_hashes(slot_hashes);
183                }
184            });
185        }
186        #[allow(deprecated)]
187        if self.recent_blockhashes.is_none() {
188            get_account_data(&RecentBlockhashes::id(), &mut |data: &[u8]| {
189                if let Ok(recent_blockhashes) = bincode::deserialize(data) {
190                    self.set_recent_blockhashes(recent_blockhashes);
191                }
192            });
193        }
194        if self.stake_history.is_none() {
195            get_account_data(&StakeHistory::id(), &mut |data: &[u8]| {
196                if let Ok(stake_history) = bincode::deserialize(data) {
197                    self.set_stake_history(stake_history);
198                }
199            });
200        }
201        if self.last_restart_slot.is_none() {
202            get_account_data(&LastRestartSlot::id(), &mut |data: &[u8]| {
203                if let Ok(last_restart_slot) = bincode::deserialize(data) {
204                    self.set_last_restart_slot(last_restart_slot);
205                }
206            });
207        }
208    }
209
210    pub fn reset(&mut self) {
211        *self = SysvarCache::default();
212    }
213}
214
215/// These methods facilitate a transition from fetching sysvars from keyed
216/// accounts to fetching from the sysvar cache without breaking consensus. In
217/// order to keep consistent behavior, they continue to enforce the same checks
218/// as `miraland_sdk::keyed_account::from_keyed_account` despite dynamically
219/// loading them instead of deserializing from account data.
220pub mod get_sysvar_with_account_check {
221    use super::*;
222
223    fn check_sysvar_account<S: Sysvar>(
224        transaction_context: &TransactionContext,
225        instruction_context: &InstructionContext,
226        instruction_account_index: IndexOfAccount,
227    ) -> Result<(), InstructionError> {
228        let index_in_transaction = instruction_context
229            .get_index_of_instruction_account_in_transaction(instruction_account_index)?;
230        if !S::check_id(transaction_context.get_key_of_account_at_index(index_in_transaction)?) {
231            return Err(InstructionError::InvalidArgument);
232        }
233        Ok(())
234    }
235
236    pub fn clock(
237        invoke_context: &InvokeContext,
238        instruction_context: &InstructionContext,
239        instruction_account_index: IndexOfAccount,
240    ) -> Result<Arc<Clock>, InstructionError> {
241        check_sysvar_account::<Clock>(
242            invoke_context.transaction_context,
243            instruction_context,
244            instruction_account_index,
245        )?;
246        invoke_context.get_sysvar_cache().get_clock()
247    }
248
249    pub fn rent(
250        invoke_context: &InvokeContext,
251        instruction_context: &InstructionContext,
252        instruction_account_index: IndexOfAccount,
253    ) -> Result<Arc<Rent>, InstructionError> {
254        check_sysvar_account::<Rent>(
255            invoke_context.transaction_context,
256            instruction_context,
257            instruction_account_index,
258        )?;
259        invoke_context.get_sysvar_cache().get_rent()
260    }
261
262    pub fn slot_hashes(
263        invoke_context: &InvokeContext,
264        instruction_context: &InstructionContext,
265        instruction_account_index: IndexOfAccount,
266    ) -> Result<Arc<SlotHashes>, InstructionError> {
267        check_sysvar_account::<SlotHashes>(
268            invoke_context.transaction_context,
269            instruction_context,
270            instruction_account_index,
271        )?;
272        invoke_context.get_sysvar_cache().get_slot_hashes()
273    }
274
275    #[allow(deprecated)]
276    pub fn recent_blockhashes(
277        invoke_context: &InvokeContext,
278        instruction_context: &InstructionContext,
279        instruction_account_index: IndexOfAccount,
280    ) -> Result<Arc<RecentBlockhashes>, InstructionError> {
281        check_sysvar_account::<RecentBlockhashes>(
282            invoke_context.transaction_context,
283            instruction_context,
284            instruction_account_index,
285        )?;
286        invoke_context.get_sysvar_cache().get_recent_blockhashes()
287    }
288
289    pub fn stake_history(
290        invoke_context: &InvokeContext,
291        instruction_context: &InstructionContext,
292        instruction_account_index: IndexOfAccount,
293    ) -> Result<Arc<StakeHistory>, InstructionError> {
294        check_sysvar_account::<StakeHistory>(
295            invoke_context.transaction_context,
296            instruction_context,
297            instruction_account_index,
298        )?;
299        invoke_context.get_sysvar_cache().get_stake_history()
300    }
301
302    pub fn last_restart_slot(
303        invoke_context: &InvokeContext,
304        instruction_context: &InstructionContext,
305        instruction_account_index: IndexOfAccount,
306    ) -> Result<Arc<LastRestartSlot>, InstructionError> {
307        check_sysvar_account::<LastRestartSlot>(
308            invoke_context.transaction_context,
309            instruction_context,
310            instruction_account_index,
311        )?;
312        invoke_context.get_sysvar_cache().get_last_restart_slot()
313    }
314}