Skip to content

Commit

Permalink
refactor get_sysvar API
Browse files Browse the repository at this point in the history
  • Loading branch information
buffalojoec committed Jun 6, 2024
1 parent bba4fca commit 031ac38
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 52 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion sdk/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ array-bytes = { workspace = true }
assert_matches = { workspace = true }
serde_json = { workspace = true }
static_assertions = { workspace = true }
test-case = { workspace = true }

[build-dependencies]
rustc_version = { workspace = true }
Expand Down
63 changes: 15 additions & 48 deletions sdk/program/src/sysvar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,9 @@
//!
//! [sysvardoc]: https://docs.solanalabs.com/runtime/sysvars
use crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
#[allow(deprecated)]
pub use sysvar_ids::ALL_IDS;
use {
crate::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey},
std::alloc::{alloc, Layout},
};

pub mod clock;
pub mod epoch_rewards;
Expand Down Expand Up @@ -254,22 +251,20 @@ macro_rules! impl_sysvar_get {

/// Handler for retrieving a slice of sysvar data from the `sol_get_sysvar`
/// syscall.
fn get_sysvar<'a>(sysvar_id: &Pubkey, offset: u64, length: u64) -> Result<&'a [u8], ProgramError> {
let sysvar_id = sysvar_id as *const _ as *const u8;

// Allocate the memory region for the sysvar data to be written to.
let var = unsafe {
let length = length as usize;
let layout = Layout::from_size_align(length, std::mem::align_of::<u8>())
.map_err(|_| ProgramError::InvalidArgument)?;
let ptr = alloc(layout);
if ptr.is_null() {
return Err(ProgramError::InvalidArgument);
}
std::slice::from_raw_parts_mut(ptr, length)
};
fn get_sysvar(
dst: &mut [u8],
sysvar_id: &Pubkey,
offset: u64,
length: u64,
) -> Result<(), ProgramError> {
// Check that the provided destination buffer is large enough to hold the
// requested data.
if dst.len() < length as usize {
return Err(ProgramError::InvalidArgument);
}

let var_addr = var as *mut _ as *mut u8;
let sysvar_id = sysvar_id as *const _ as *const u8;
let var_addr = dst as *mut _ as *mut u8;

#[cfg(target_os = "solana")]
let result = unsafe { crate::syscalls::sol_get_sysvar(sysvar_id, var_addr, offset, length) };
Expand All @@ -278,7 +273,7 @@ fn get_sysvar<'a>(sysvar_id: &Pubkey, offset: u64, length: u64) -> Result<&'a [u
let result = crate::program_stubs::sol_get_sysvar(sysvar_id, var_addr, offset, length);

match result {
crate::entrypoint::SUCCESS => Ok(var),
crate::entrypoint::SUCCESS => Ok(()),
e => Err(e.into()),
}
}
Expand All @@ -289,7 +284,6 @@ mod tests {
super::*,
crate::{clock::Epoch, program_error::ProgramError, pubkey::Pubkey},
std::{cell::RefCell, rc::Rc},
test_case::test_case,
};

#[repr(C)]
Expand Down Expand Up @@ -342,31 +336,4 @@ mod tests {
account_info.data = Rc::new(RefCell::new(&mut small_data));
assert_eq!(test_sysvar.to_account_info(&mut account_info), None);
}

#[allow(deprecated)]
#[test_case(0)]
#[test_case(clock::Clock::size_of() as u64)]
#[test_case(epoch_rewards::EpochRewards::size_of() as u64)]
#[test_case(epoch_schedule::EpochSchedule::size_of() as u64)]
#[test_case(fees::Fees::size_of() as u64)]
#[test_case(last_restart_slot::LastRestartSlot::size_of() as u64)]
#[test_case(rent::Rent::size_of() as u64)]
#[test_case(slot_hashes::SlotHashes::size_of() as u64)]
#[test_case(slot_history::SlotHistory::size_of() as u64)]
#[test_case(stake_history::StakeHistory::size_of() as u64)]
fn test_get_sysvar_alloc(length: u64) {
// As long as we use a test sysvar ID and we're _not_ testing from a BPF
// context, the `sol_get_sysvar` syscall will always return
// `ProgramError::UnsupportedSysvar`, since the ID will not match any
// sysvars in the Sysvar Cache.
// Under this condition, we can test the pointer allocation by ensuring
// the allocation routes to `ProgramError::UnsupportedSysvar` and does
// not throw a panic or `ProgramError::InvalidArgument`.
// Also, `offset` is only used in the syscall, _not_ the pointer
// allocation.
assert_eq!(
get_sysvar(&crate::sysvar::tests::id(), 0, length).unwrap_err(),
ProgramError::UnsupportedSysvar,
);
}
}
8 changes: 6 additions & 2 deletions sdk/program/src/sysvar/slot_hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ pub trait SlotHashesSysvar {
/// Get a value from the sysvar entries by its key.
/// Returns `None` if the key is not found.
fn get(slot: &Slot) -> Result<Option<Hash>, ProgramError> {
let data = get_sysvar(&SlotHashes::id(), 0, SlotHashes::size_of() as u64)?;
let data_len = SlotHashes::size_of();
let mut data = vec![0u8; data_len];
get_sysvar(&mut data, &SlotHashes::id(), 0, data_len as u64)?;
let pod_hashes: &[PodSlotHash] =
bytemuck::try_cast_slice(&data[8..]).map_err(|_| ProgramError::InvalidArgument)?;

Expand All @@ -96,7 +98,9 @@ pub trait SlotHashesSysvar {
/// Get the position of an entry in the sysvar by its key.
/// Returns `None` if the key is not found.
fn position(slot: &Slot) -> Result<Option<usize>, ProgramError> {
let data = get_sysvar(&SlotHashes::id(), 0, SlotHashes::size_of() as u64)?;
let data_len = SlotHashes::size_of();
let mut data = vec![0u8; data_len];
get_sysvar(&mut data, &SlotHashes::id(), 0, data_len as u64)?;
let pod_hashes: &[PodSlotHash] =
bytemuck::try_cast_slice(&data[8..]).map_err(|_| ProgramError::InvalidArgument)?;

Expand Down

0 comments on commit 031ac38

Please sign in to comment.