cbe_program_runtime/
sysvar_cache.rs

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