solana_program/sysvar/
mod.rs1use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
85#[deprecated(since = "2.1.0", note = "Use `solana-sysvar-id` crate instead")]
86pub use solana_sysvar_id::{
87 check_id, declare_deprecated_sysvar_id, declare_sysvar_id, id, SysvarId, ID,
88};
89#[allow(deprecated)]
90pub use sysvar_ids::ALL_IDS;
91
92pub mod clock;
93pub mod epoch_rewards;
94pub mod epoch_schedule;
95pub mod fees;
96pub mod instructions;
97pub mod last_restart_slot;
98pub mod recent_blockhashes;
99pub mod rent;
100pub mod rewards;
101pub mod slot_hashes;
102pub mod slot_history;
103pub mod stake_history;
104
105#[deprecated(
106 since = "2.0.0",
107 note = "please use `solana_sdk::reserved_account_keys::ReservedAccountKeys` instead"
108)]
109mod sysvar_ids {
110 use {super::*, lazy_static::lazy_static};
111 lazy_static! {
112 pub static ref ALL_IDS: Vec<Pubkey> = vec![
114 clock::id(),
115 epoch_schedule::id(),
116 #[allow(deprecated)]
117 fees::id(),
118 #[allow(deprecated)]
119 recent_blockhashes::id(),
120 rent::id(),
121 rewards::id(),
122 slot_hashes::id(),
123 slot_history::id(),
124 stake_history::id(),
125 instructions::id(),
126 ];
127 }
128}
129
130#[deprecated(
132 since = "2.0.0",
133 note = "please check the account's owner or use solana_sdk::reserved_account_keys::ReservedAccountKeys instead"
134)]
135#[allow(deprecated)]
136pub fn is_sysvar_id(id: &Pubkey) -> bool {
137 ALL_IDS.iter().any(|key| key == id)
138}
139
140pub trait Sysvar:
142 SysvarId + Default + Sized + serde::Serialize + serde::de::DeserializeOwned
143{
144 fn size_of() -> usize {
146 bincode::serialized_size(&Self::default()).unwrap() as usize
147 }
148
149 fn from_account_info(account_info: &AccountInfo) -> Result<Self, ProgramError> {
156 if !Self::check_id(account_info.unsigned_key()) {
157 return Err(ProgramError::InvalidArgument);
158 }
159 bincode::deserialize(&account_info.data.borrow()).map_err(|_| ProgramError::InvalidArgument)
160 }
161
162 fn to_account_info(&self, account_info: &mut AccountInfo) -> Option<()> {
168 bincode::serialize_into(&mut account_info.data.borrow_mut()[..], self).ok()
169 }
170
171 fn get() -> Result<Self, ProgramError> {
180 Err(ProgramError::UnsupportedSysvar)
181 }
182}
183
184#[macro_export]
186macro_rules! impl_sysvar_get {
187 ($syscall_name:ident) => {
188 fn get() -> Result<Self, ProgramError> {
189 let mut var = Self::default();
190 let var_addr = &mut var as *mut _ as *mut u8;
191
192 #[cfg(target_os = "solana")]
193 let result = unsafe { $crate::syscalls::$syscall_name(var_addr) };
194
195 #[cfg(not(target_os = "solana"))]
196 let result = $crate::program_stubs::$syscall_name(var_addr);
197
198 match result {
199 $crate::entrypoint::SUCCESS => Ok(var),
200 e => Err(e.into()),
201 }
202 }
203 };
204}
205
206fn get_sysvar(
209 dst: &mut [u8],
210 sysvar_id: &Pubkey,
211 offset: u64,
212 length: u64,
213) -> Result<(), ProgramError> {
214 if dst.len() < length as usize {
217 return Err(ProgramError::InvalidArgument);
218 }
219
220 let sysvar_id = sysvar_id as *const _ as *const u8;
221 let var_addr = dst as *mut _ as *mut u8;
222
223 #[cfg(target_os = "solana")]
224 let result = unsafe { crate::syscalls::sol_get_sysvar(sysvar_id, var_addr, offset, length) };
225
226 #[cfg(not(target_os = "solana"))]
227 let result = crate::program_stubs::sol_get_sysvar(sysvar_id, var_addr, offset, length);
228
229 match result {
230 crate::entrypoint::SUCCESS => Ok(()),
231 e => Err(e.into()),
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use {
238 super::*,
239 crate::{
240 entrypoint::SUCCESS,
241 program_error::ProgramError,
242 program_stubs::{set_syscall_stubs, SyscallStubs},
243 pubkey::Pubkey,
244 },
245 solana_clock::Epoch,
246 std::{cell::RefCell, rc::Rc},
247 };
248
249 #[repr(C)]
250 #[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
251 struct TestSysvar {
252 something: Pubkey,
253 }
254 crate::declare_id!("TestSysvar111111111111111111111111111111111");
255 impl crate::sysvar::SysvarId for TestSysvar {
256 fn id() -> crate::pubkey::Pubkey {
257 id()
258 }
259
260 fn check_id(pubkey: &crate::pubkey::Pubkey) -> bool {
261 check_id(pubkey)
262 }
263 }
264 impl Sysvar for TestSysvar {}
265
266 struct MockGetSysvarSyscall {
268 data: Vec<u8>,
269 }
270 impl SyscallStubs for MockGetSysvarSyscall {
271 #[allow(clippy::arithmetic_side_effects)]
272 fn sol_get_sysvar(
273 &self,
274 _sysvar_id_addr: *const u8,
275 var_addr: *mut u8,
276 offset: u64,
277 length: u64,
278 ) -> u64 {
279 let slice = unsafe { std::slice::from_raw_parts_mut(var_addr, length as usize) };
280 slice.copy_from_slice(&self.data[offset as usize..(offset + length) as usize]);
281 SUCCESS
282 }
283 }
284 pub fn mock_get_sysvar_syscall(data: &[u8]) {
285 set_syscall_stubs(Box::new(MockGetSysvarSyscall {
286 data: data.to_vec(),
287 }));
288 }
289
290 #[test]
291 fn test_sysvar_account_info_to_from() {
292 let test_sysvar = TestSysvar::default();
293 let key = crate::sysvar::tests::id();
294 let wrong_key = Pubkey::new_unique();
295 let owner = Pubkey::new_unique();
296 let mut lamports = 42;
297 let mut data = vec![0_u8; TestSysvar::size_of()];
298 let mut account_info = AccountInfo::new(
299 &key,
300 false,
301 true,
302 &mut lamports,
303 &mut data,
304 &owner,
305 false,
306 Epoch::default(),
307 );
308
309 test_sysvar.to_account_info(&mut account_info).unwrap();
310 let new_test_sysvar = TestSysvar::from_account_info(&account_info).unwrap();
311 assert_eq!(test_sysvar, new_test_sysvar);
312
313 account_info.key = &wrong_key;
314 assert_eq!(
315 TestSysvar::from_account_info(&account_info),
316 Err(ProgramError::InvalidArgument)
317 );
318
319 let mut small_data = vec![];
320 account_info.data = Rc::new(RefCell::new(&mut small_data));
321 assert_eq!(test_sysvar.to_account_info(&mut account_info), None);
322 }
323}