clone_solana_sysvar/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
3//! Access to special accounts with dynamically-updated data.
4//!
5//! Sysvars are special accounts that contain dynamically-updated data about the
6//! network cluster, the blockchain history, and the executing transaction. Each
7//! sysvar is defined in its own submodule within this module. The [`clock`],
8//! [`epoch_schedule`], [`instructions`], and [`rent`] sysvars are most useful
9//! to on-chain programs.
10//!
11//! Simple sysvars implement the [`Sysvar::get`] method, which loads a sysvar
12//! directly from the runtime, as in this example that logs the `clock` sysvar:
13//!
14//! ```
15//! use clone_solana_account_info::AccountInfo;
16//! use clone_solana_msg::msg;
17//! use clone_solana_sysvar::Sysvar;
18//! use clone_solana_program_error::ProgramResult;
19//! use clone_solana_pubkey::Pubkey;
20//!
21//! fn process_instruction(
22//!     program_id: &Pubkey,
23//!     accounts: &[AccountInfo],
24//!     instruction_data: &[u8],
25//! ) -> ProgramResult {
26//!     let clock = clone_solana_clock::Clock::get()?;
27//!     msg!("clock: {:#?}", clock);
28//!     Ok(())
29//! }
30//! ```
31//!
32//! Since Solana sysvars are accounts, if the `AccountInfo` is provided to the
33//! program, then the program can deserialize the sysvar with
34//! [`Sysvar::from_account_info`] to access its data, as in this example that
35//! again logs the [`clock`] sysvar.
36//!
37//! ```
38//! use clone_solana_account_info::{AccountInfo, next_account_info};
39//! use clone_solana_msg::msg;
40//! use clone_solana_sysvar::Sysvar;
41//! use clone_solana_program_error::ProgramResult;
42//! use clone_solana_pubkey::Pubkey;
43//!
44//! fn process_instruction(
45//!     program_id: &Pubkey,
46//!     accounts: &[AccountInfo],
47//!     instruction_data: &[u8],
48//! ) -> ProgramResult {
49//!     let account_info_iter = &mut accounts.iter();
50//!     let clock_account = next_account_info(account_info_iter)?;
51//!     let clock = clone_solana_clock::Clock::from_account_info(&clock_account)?;
52//!     msg!("clock: {:#?}", clock);
53//!     Ok(())
54//! }
55//! ```
56//!
57//! When possible, programs should prefer to call `Sysvar::get` instead of
58//! deserializing with `Sysvar::from_account_info`, as the latter imposes extra
59//! overhead of deserialization while also requiring the sysvar account address
60//! be passed to the program, wasting the limited space available to
61//! transactions. Deserializing sysvars that can instead be retrieved with
62//! `Sysvar::get` should be only be considered for compatibility with older
63//! programs that pass around sysvar accounts.
64//!
65//! Some sysvars are too large to deserialize within a program, and
66//! `Sysvar::from_account_info` returns an error, or the serialization attempt
67//! will exhaust the program's compute budget. Some sysvars do not implement
68//! `Sysvar::get` and return an error. Some sysvars have custom deserializers
69//! that do not implement the `Sysvar` trait. These cases are documented in the
70//! modules for individual sysvars.
71//!
72//! All sysvar accounts are owned by the account identified by [`sysvar::ID`].
73//!
74//! [`sysvar::ID`]: https://docs.rs/solana-sdk-ids/latest/clone_solana_sdk_ids/sysvar/constant.ID.html
75//!
76//! For more details see the Solana [documentation on sysvars][sysvardoc].
77//!
78//! [sysvardoc]: https://docs.solanalabs.com/runtime/sysvars
79
80// hidden re-exports to make macros work
81pub mod __private {
82    #[cfg(target_os = "solana")]
83    pub use clone_solana_define_syscall::definitions;
84    pub use {clone_solana_program_entrypoint::SUCCESS, clone_solana_program_error::ProgramError};
85}
86use clone_solana_pubkey::Pubkey;
87#[allow(deprecated)]
88#[doc(inline)]
89#[deprecated(
90    since = "2.0.0",
91    note = "please use `clone_solana_sdk::reserved_account_keys::ReservedAccountKeys` instead"
92)]
93pub use sysvar_ids::ALL_IDS;
94#[cfg(feature = "bincode")]
95use {
96    clone_solana_account_info::AccountInfo, clone_solana_program_error::ProgramError,
97    clone_solana_sysvar_id::SysvarId,
98};
99
100pub mod clock;
101pub mod epoch_rewards;
102pub mod epoch_schedule;
103pub mod fees;
104pub mod last_restart_slot;
105pub mod program_stubs;
106pub mod recent_blockhashes;
107pub mod rent;
108pub mod rewards;
109pub mod slot_hashes;
110pub mod slot_history;
111pub mod stake_history;
112
113#[deprecated(
114    since = "2.0.0",
115    note = "please use `clone_solana_sdk::reserved_account_keys::ReservedAccountKeys` instead"
116)]
117mod sysvar_ids {
118    use {super::*, lazy_static::lazy_static};
119    lazy_static! {
120        // This will be deprecated and so this list shouldn't be modified
121        pub static ref ALL_IDS: Vec<Pubkey> = vec![
122            clock::id(),
123            epoch_schedule::id(),
124            #[allow(deprecated)]
125            fees::id(),
126            #[allow(deprecated)]
127            recent_blockhashes::id(),
128            rent::id(),
129            rewards::id(),
130            slot_hashes::id(),
131            slot_history::id(),
132            stake_history::id(),
133            clone_solana_sdk_ids::sysvar::instructions::id(),
134        ];
135    }
136}
137
138/// Returns `true` of the given `Pubkey` is a sysvar account.
139#[deprecated(
140    since = "2.0.0",
141    note = "please check the account's owner or use clone_solana_sdk::reserved_account_keys::ReservedAccountKeys instead"
142)]
143#[allow(deprecated)]
144pub fn is_sysvar_id(id: &Pubkey) -> bool {
145    ALL_IDS.iter().any(|key| key == id)
146}
147
148#[cfg(feature = "bincode")]
149/// A type that holds sysvar data.
150pub trait Sysvar:
151    SysvarId + Default + Sized + serde::Serialize + serde::de::DeserializeOwned
152{
153    /// The size in bytes of the sysvar as serialized account data.
154    fn size_of() -> usize {
155        bincode::serialized_size(&Self::default()).unwrap() as usize
156    }
157
158    /// Deserializes the sysvar from its `AccountInfo`.
159    ///
160    /// # Errors
161    ///
162    /// If `account_info` does not have the same ID as the sysvar this function
163    /// returns [`ProgramError::InvalidArgument`].
164    fn from_account_info(account_info: &AccountInfo) -> Result<Self, ProgramError> {
165        if !Self::check_id(account_info.unsigned_key()) {
166            return Err(ProgramError::InvalidArgument);
167        }
168        bincode::deserialize(&account_info.data.borrow()).map_err(|_| ProgramError::InvalidArgument)
169    }
170
171    /// Serializes the sysvar to `AccountInfo`.
172    ///
173    /// # Errors
174    ///
175    /// Returns `None` if serialization failed.
176    fn to_account_info(&self, account_info: &mut AccountInfo) -> Option<()> {
177        bincode::serialize_into(&mut account_info.data.borrow_mut()[..], self).ok()
178    }
179
180    /// Load the sysvar directly from the runtime.
181    ///
182    /// This is the preferred way to load a sysvar. Calling this method does not
183    /// incur any deserialization overhead, and does not require the sysvar
184    /// account to be passed to the program.
185    ///
186    /// Not all sysvars support this method. If not, it returns
187    /// [`ProgramError::UnsupportedSysvar`].
188    fn get() -> Result<Self, ProgramError> {
189        Err(ProgramError::UnsupportedSysvar)
190    }
191}
192
193/// Implements the [`Sysvar::get`] method for both SBF and host targets.
194#[macro_export]
195macro_rules! impl_sysvar_get {
196    ($syscall_name:ident) => {
197        fn get() -> Result<Self, $crate::__private::ProgramError> {
198            let mut var = Self::default();
199            let var_addr = &mut var as *mut _ as *mut u8;
200
201            #[cfg(target_os = "solana")]
202            let result = unsafe { $crate::__private::definitions::$syscall_name(var_addr) };
203
204            #[cfg(not(target_os = "solana"))]
205            let result = $crate::program_stubs::$syscall_name(var_addr);
206
207            match result {
208                $crate::__private::SUCCESS => Ok(var),
209                e => Err(e.into()),
210            }
211        }
212    };
213}
214
215/// Handler for retrieving a slice of sysvar data from the `sol_get_sysvar`
216/// syscall.
217fn get_sysvar(
218    dst: &mut [u8],
219    sysvar_id: &Pubkey,
220    offset: u64,
221    length: u64,
222) -> Result<(), clone_solana_program_error::ProgramError> {
223    // Check that the provided destination buffer is large enough to hold the
224    // requested data.
225    if dst.len() < length as usize {
226        return Err(clone_solana_program_error::ProgramError::InvalidArgument);
227    }
228
229    let sysvar_id = sysvar_id as *const _ as *const u8;
230    let var_addr = dst as *mut _ as *mut u8;
231
232    #[cfg(target_os = "solana")]
233    let result = unsafe {
234        clone_solana_define_syscall::definitions::sol_get_sysvar(
235            sysvar_id, var_addr, offset, length,
236        )
237    };
238
239    #[cfg(not(target_os = "solana"))]
240    let result = crate::program_stubs::sol_get_sysvar(sysvar_id, var_addr, offset, length);
241
242    match result {
243        clone_solana_program_entrypoint::SUCCESS => Ok(()),
244        e => Err(e.into()),
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use {
251        super::*,
252        crate::program_stubs::{set_syscall_stubs, SyscallStubs},
253        clone_solana_clock::Epoch,
254        clone_solana_program_entrypoint::SUCCESS,
255        clone_solana_program_error::ProgramError,
256        clone_solana_pubkey::Pubkey,
257        serde_derive::{Deserialize, Serialize},
258        std::{cell::RefCell, rc::Rc},
259    };
260
261    #[repr(C)]
262    #[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
263    struct TestSysvar {
264        something: Pubkey,
265    }
266    clone_solana_pubkey::declare_id!("TestSysvar111111111111111111111111111111111");
267    impl clone_solana_sysvar_id::SysvarId for TestSysvar {
268        fn id() -> clone_solana_pubkey::Pubkey {
269            id()
270        }
271
272        fn check_id(pubkey: &clone_solana_pubkey::Pubkey) -> bool {
273            check_id(pubkey)
274        }
275    }
276    impl Sysvar for TestSysvar {}
277
278    // NOTE tests that use this mock MUST carry the #[serial] attribute
279    struct MockGetSysvarSyscall {
280        data: Vec<u8>,
281    }
282    impl SyscallStubs for MockGetSysvarSyscall {
283        #[allow(clippy::arithmetic_side_effects)]
284        fn sol_get_sysvar(
285            &self,
286            _sysvar_id_addr: *const u8,
287            var_addr: *mut u8,
288            offset: u64,
289            length: u64,
290        ) -> u64 {
291            let slice = unsafe { std::slice::from_raw_parts_mut(var_addr, length as usize) };
292            slice.copy_from_slice(&self.data[offset as usize..(offset + length) as usize]);
293            SUCCESS
294        }
295    }
296    pub fn mock_get_sysvar_syscall(data: &[u8]) {
297        set_syscall_stubs(Box::new(MockGetSysvarSyscall {
298            data: data.to_vec(),
299        }));
300    }
301
302    #[test]
303    fn test_sysvar_account_info_to_from() {
304        let test_sysvar = TestSysvar::default();
305        let key = id();
306        let wrong_key = Pubkey::new_unique();
307        let owner = Pubkey::new_unique();
308        let mut lamports = 42;
309        let mut data = vec![0_u8; TestSysvar::size_of()];
310        let mut account_info = AccountInfo::new(
311            &key,
312            false,
313            true,
314            &mut lamports,
315            &mut data,
316            &owner,
317            false,
318            Epoch::default(),
319        );
320
321        test_sysvar.to_account_info(&mut account_info).unwrap();
322        let new_test_sysvar = TestSysvar::from_account_info(&account_info).unwrap();
323        assert_eq!(test_sysvar, new_test_sysvar);
324
325        account_info.key = &wrong_key;
326        assert_eq!(
327            TestSysvar::from_account_info(&account_info),
328            Err(ProgramError::InvalidArgument)
329        );
330
331        let mut small_data = vec![];
332        account_info.data = Rc::new(RefCell::new(&mut small_data));
333        assert_eq!(test_sysvar.to_account_info(&mut account_info), None);
334    }
335}