|
| 1 | +//! Access to the `sol_get_sysvar` syscall, used to fetch sysvar data from the runtime. |
| 2 | +//! |
| 3 | +//! On-chain calls go directly to the syscall. Off-chain calls are routed through a |
| 4 | +//! stub which test harnesses can install with [`set_get_sysvar_stub`] to serve mock |
| 5 | +//! sysvar data. |
| 6 | +#![no_std] |
| 7 | +#![cfg_attr(docsrs, feature(doc_cfg))] |
| 8 | + |
| 9 | +#[cfg(all(not(target_os = "solana"), feature = "std"))] |
| 10 | +extern crate std; |
| 11 | + |
| 12 | +use {solana_address::Address, solana_program_error::ProgramError}; |
| 13 | + |
| 14 | +#[cfg(all(not(target_os = "solana"), feature = "std"))] |
| 15 | +mod stubs; |
| 16 | + |
| 17 | +#[cfg(all(not(target_os = "solana"), feature = "std"))] |
| 18 | +pub use stubs::{clear_get_sysvar_stub, set_get_sysvar_stub, GetSysvarStub}; |
| 19 | + |
| 20 | +/// Syscall success code. |
| 21 | +// |
| 22 | +// Defined in solana-program-entrypoint as [`SUCCESS`](https://github.com/anza-xyz/solana-sdk/blob/program-entrypoint@v2.2.1/program-entrypoint/src/lib.rs#L35). |
| 23 | +const SUCCESS: u64 = 0; |
| 24 | +/// Return value indicating that the `offset + length` is greater than the length of |
| 25 | +/// the sysvar data. |
| 26 | +// |
| 27 | +// Defined in the Agave syscalls crate as [`OFFSET_LENGTH_EXCEEDS_SYSVAR`](https://github.com/anza-xyz/agave/blob/v4.0.2/syscalls/src/sysvar.rs#L180). |
| 28 | +const OFFSET_LENGTH_EXCEEDS_SYSVAR: u64 = 1; |
| 29 | + |
| 30 | +/// Return value indicating that the sysvar was not found. |
| 31 | +// |
| 32 | +// Defined in the Agave syscalls crate as [`SYSVAR_NOT_FOUND`](https://github.com/anza-xyz/agave/blob/v4.0.2/syscalls/src/sysvar.rs#L179). |
| 33 | +const SYSVAR_NOT_FOUND: u64 = 2; |
| 34 | + |
| 35 | +/// Handler for retrieving a slice of sysvar data from the `sol_get_sysvar` |
| 36 | +/// syscall. |
| 37 | +pub fn get_sysvar( |
| 38 | + dst: &mut [u8], |
| 39 | + sysvar_id: &Address, |
| 40 | + offset: u64, |
| 41 | + length: u64, |
| 42 | +) -> Result<(), ProgramError> { |
| 43 | + // Check that the provided destination buffer is large enough to hold the requested data |
| 44 | + if dst.len() < length as usize { |
| 45 | + return Err(ProgramError::InvalidArgument); |
| 46 | + } |
| 47 | + |
| 48 | + let sysvar_id = sysvar_id as *const _ as *const u8; |
| 49 | + let var_addr = dst as *mut _ as *mut u8; |
| 50 | + |
| 51 | + match sol_get_sysvar(sysvar_id, var_addr, offset, length) { |
| 52 | + SUCCESS => Ok(()), |
| 53 | + OFFSET_LENGTH_EXCEEDS_SYSVAR => Err(ProgramError::InvalidArgument), |
| 54 | + _ => Err(ProgramError::UnsupportedSysvar), |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +/// Internal helper for retrieving sysvar data directly into a raw buffer. |
| 59 | +/// |
| 60 | +/// # Safety |
| 61 | +/// |
| 62 | +/// This function bypasses the slice-length check that `get_sysvar` performs. |
| 63 | +/// The caller must ensure that `var_addr` points to a writable buffer of at |
| 64 | +/// least `length` bytes. This is typically used with `MaybeUninit` to load |
| 65 | +/// compact representations of sysvars. |
| 66 | +#[doc(hidden)] |
| 67 | +pub unsafe fn get_sysvar_unchecked( |
| 68 | + var_addr: *mut u8, |
| 69 | + sysvar_id: *const u8, |
| 70 | + offset: u64, |
| 71 | + length: u64, |
| 72 | +) -> Result<(), ProgramError> { |
| 73 | + match sol_get_sysvar(sysvar_id, var_addr, offset, length) { |
| 74 | + SUCCESS => Ok(()), |
| 75 | + OFFSET_LENGTH_EXCEEDS_SYSVAR => Err(ProgramError::InvalidArgument), |
| 76 | + SYSVAR_NOT_FOUND => Err(ProgramError::UnsupportedSysvar), |
| 77 | + // Unexpected errors are folded into `UnsupportedSysvar`. |
| 78 | + _ => Err(ProgramError::UnsupportedSysvar), |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +// Dispatch to the syscall backend available for this target and feature set |
| 83 | +fn sol_get_sysvar(sysvar_id: *const u8, var_addr: *mut u8, offset: u64, length: u64) -> u64 { |
| 84 | + // On-chain programs call the runtime syscall directly |
| 85 | + #[cfg(target_os = "solana")] |
| 86 | + unsafe { |
| 87 | + solana_define_syscall::definitions::sol_get_sysvar(sysvar_id, var_addr, offset, length) |
| 88 | + } |
| 89 | + |
| 90 | + // Off-chain std builds route through the host-test stub registry |
| 91 | + #[cfg(all(not(target_os = "solana"), feature = "std"))] |
| 92 | + { |
| 93 | + stubs::sol_get_sysvar(sysvar_id, var_addr, offset, length) |
| 94 | + } |
| 95 | + |
| 96 | + // Off-chain no-std builds have neither runtime syscalls nor host stubs. |
| 97 | + #[cfg(all(not(target_os = "solana"), not(feature = "std")))] |
| 98 | + { |
| 99 | + let _ = (sysvar_id, var_addr, offset, length); |
| 100 | + solana_program_error::UNSUPPORTED_SYSVAR |
| 101 | + } |
| 102 | +} |
0 commit comments