Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions define-syscall/src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ define_syscall!(fn sol_get_sysvar(sysvar_id_addr: *const u8, result: *mut u8, of
define_syscall!(fn sol_get_epoch_stake(vote_address: *const u8) -> u64);
define_syscall!(fn sol_panic_(filename: *const u8, filename_len: u64, line: u64, column: u64));

// these are to be deprecated once they are superceded by sol_get_sysvar
define_syscall!(fn sol_get_clock_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_epoch_schedule_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_rent_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_last_restart_slot(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_epoch_rewards_sysvar(addr: *mut u8) -> u64);
// these are deprecated - use sol_get_sysvar instead
define_syscall!(#[deprecated(since = "3.0.0", note = "Use `sol_get_sysvar` with `Clock` sysvar address instead")] fn sol_get_clock_sysvar(addr: *mut u8) -> u64);
define_syscall!(#[deprecated(since = "3.0.0", note = "Use `sol_get_sysvar` with `EpochSchedule` sysvar address instead")] fn sol_get_epoch_schedule_sysvar(addr: *mut u8) -> u64);
define_syscall!(#[deprecated(since = "3.0.0", note = "Use `sol_get_sysvar` with `Rent` sysvar address instead")] fn sol_get_rent_sysvar(addr: *mut u8) -> u64);
define_syscall!(#[deprecated(since = "3.0.0", note = "Use `sol_get_sysvar` with `LastRestartSlot` sysvar address instead")] fn sol_get_last_restart_slot(addr: *mut u8) -> u64);
define_syscall!(#[deprecated(since = "3.0.0", note = "Use `sol_get_sysvar` with `EpochRewards` sysvar address instead")] fn sol_get_epoch_rewards_sysvar(addr: *mut u8) -> u64);

// this cannot go through sol_get_sysvar but can be removed once no longer in use
define_syscall!(fn sol_get_fees_sysvar(addr: *mut u8) -> u64);
14 changes: 8 additions & 6 deletions define-syscall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ pub mod definitions;
#[cfg(target_feature = "static-syscalls")]
#[macro_export]
macro_rules! define_syscall {
(fn $name:ident($($arg:ident: $typ:ty),*) -> $ret:ty) => {
($(#[$attr:meta])* fn $name:ident($($arg:ident: $typ:ty),*) -> $ret:ty) => {
$(#[$attr])*
#[inline]
pub unsafe fn $name($($arg: $typ),*) -> $ret {
// this enum is used to force the hash to be computed in a const context
Expand All @@ -19,21 +20,22 @@ macro_rules! define_syscall {
}

};
(fn $name:ident($($arg:ident: $typ:ty),*)) => {
define_syscall!(fn $name($($arg: $typ),*) -> ());
($(#[$attr:meta])* fn $name:ident($($arg:ident: $typ:ty),*)) => {
define_syscall!($(#[$attr])* fn $name($($arg: $typ),*) -> ());
}
}

#[cfg(not(target_feature = "static-syscalls"))]
#[macro_export]
macro_rules! define_syscall {
(fn $name:ident($($arg:ident: $typ:ty),*) -> $ret:ty) => {
($(#[$attr:meta])* fn $name:ident($($arg:ident: $typ:ty),*) -> $ret:ty) => {
extern "C" {
$(#[$attr])*
pub fn $name($($arg: $typ),*) -> $ret;
}
};
(fn $name:ident($($arg:ident: $typ:ty),*)) => {
define_syscall!(fn $name($($arg: $typ),*) -> ());
($(#[$attr:meta])* fn $name:ident($($arg:ident: $typ:ty),*)) => {
define_syscall!($(#[$attr])* fn $name($($arg: $typ),*) -> ());
}
}

Expand Down
68 changes: 67 additions & 1 deletion sysvar/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,74 @@ pub use {
};

impl Sysvar for Clock {
impl_sysvar_get!(sol_get_clock_sysvar);
impl_sysvar_get!(id());
}

#[cfg(feature = "bincode")]
impl SysvarSerialize for Clock {}

#[cfg(test)]
mod tests {
use {super::*, crate::tests::to_bytes, serial_test::serial};

#[test]
#[serial]
fn test_clock_get_uses_sysvar_syscall() {
let expected = Clock {
slot: 1,
epoch_start_timestamp: 2,
epoch: 3,
leader_schedule_epoch: 4,
unix_timestamp: 5,
};

let data = to_bytes(&expected);
crate::tests::mock_get_sysvar_syscall(&data);

let got = Clock::get().unwrap();
assert_eq!(got, expected);
}

struct ValidateIdSyscall {
data: Vec<u8>,
}

impl crate::program_stubs::SyscallStubs for ValidateIdSyscall {
fn sol_get_sysvar(
&self,
sysvar_id_addr: *const u8,
var_addr: *mut u8,
offset: u64,
length: u64,
) -> u64 {
// Validate that the macro passed the correct sysvar id pointer
let passed_id = unsafe { *(sysvar_id_addr as *const solana_pubkey::Pubkey) };
assert_eq!(passed_id, id());

let slice = unsafe { std::slice::from_raw_parts_mut(var_addr, length as usize) };
slice.copy_from_slice(
&self.data[offset as usize..(offset.saturating_add(length)) as usize],
);
solana_program_entrypoint::SUCCESS
}
}

#[test]
#[serial]
fn test_clock_get_passes_correct_sysvar_id() {
let expected = Clock {
slot: 11,
epoch_start_timestamp: 22,
epoch: 33,
leader_schedule_epoch: 44,
unix_timestamp: 55,
};
let data = to_bytes(&expected);
let prev = crate::program_stubs::set_syscall_stubs(Box::new(ValidateIdSyscall { data }));

let got = Clock::get().unwrap();
assert_eq!(got, expected);

let _ = crate::program_stubs::set_syscall_stubs(prev);
}
}
27 changes: 26 additions & 1 deletion sysvar/src/epoch_rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,33 @@ pub use {
};

impl Sysvar for EpochRewards {
impl_sysvar_get!(sol_get_epoch_rewards_sysvar);
impl_sysvar_get!(id());
}

#[cfg(feature = "bincode")]
impl SysvarSerialize for EpochRewards {}

#[cfg(test)]
mod tests {
use {super::*, crate::tests::to_bytes, serial_test::serial};

#[test]
#[serial]
fn test_epoch_rewards_get_uses_sysvar_syscall() {
let expected = EpochRewards {
distribution_starting_block_height: 42,
num_partitions: 7,
parent_blockhash: solana_hash::Hash::new_unique(),
total_points: 1234567890,
total_rewards: 100,
distributed_rewards: 10,
active: true,
};

let data = to_bytes(&expected);
crate::tests::mock_get_sysvar_syscall(&data);

let got = EpochRewards::get().unwrap();
assert_eq!(got, expected);
}
}
18 changes: 17 additions & 1 deletion sysvar/src/epoch_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,24 @@ pub use {
};

impl Sysvar for EpochSchedule {
impl_sysvar_get!(sol_get_epoch_schedule_sysvar);
impl_sysvar_get!(id());
}

#[cfg(feature = "bincode")]
impl SysvarSerialize for EpochSchedule {}

#[cfg(test)]
mod tests {
use {super::*, crate::tests::to_bytes, serial_test::serial};

#[test]
#[serial]
fn test_epoch_schedule_get_uses_sysvar_syscall() {
let expected = EpochSchedule::custom(1234, 5678, false);
let data = to_bytes(&expected);
crate::tests::mock_get_sysvar_syscall(&data);

let got = EpochSchedule::get().unwrap();
assert_eq!(got, expected);
}
}
48 changes: 47 additions & 1 deletion sysvar/src/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl SysvarSerialize for Fees {}

#[cfg(test)]
mod tests {
use super::*;
use {super::*, serial_test::serial};

#[test]
fn test_clone() {
Expand All @@ -76,4 +76,50 @@ mod tests {
let cloned_fees = fees.clone();
assert_eq!(cloned_fees, fees);
}

struct MockFeesSyscall;
impl crate::program_stubs::SyscallStubs for MockFeesSyscall {
fn sol_get_fees_sysvar(&self, var_addr: *mut u8) -> u64 {
let fees = Fees {
fee_calculator: FeeCalculator {
lamports_per_signature: 42,
},
};
unsafe {
std::ptr::copy_nonoverlapping(
&fees as *const _ as *const u8,
var_addr,
core::mem::size_of::<Fees>(),
);
}
solana_program_entrypoint::SUCCESS
}
}

#[test]
#[serial]
fn test_fees_get_deprecated_syscall_path() {
let _ = crate::program_stubs::set_syscall_stubs(Box::new(MockFeesSyscall));
let got = Fees::get().unwrap();
assert_eq!(got.fee_calculator.lamports_per_signature, 42);
}

struct FailFeesSyscall;
impl crate::program_stubs::SyscallStubs for FailFeesSyscall {
fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 {
9999
}
}

#[test]
#[serial]
fn test_fees_get_deprecated_non_success_maps_to_unsupported() {
let prev = crate::program_stubs::set_syscall_stubs(Box::new(FailFeesSyscall));
let got = Fees::get();
assert_eq!(
got,
Err(solana_program_error::ProgramError::UnsupportedSysvar)
);
let _ = crate::program_stubs::set_syscall_stubs(prev);
}
}
20 changes: 19 additions & 1 deletion sysvar/src/last_restart_slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,26 @@ pub use {
};

impl Sysvar for LastRestartSlot {
impl_sysvar_get!(sol_get_last_restart_slot);
impl_sysvar_get!(id());
}

#[cfg(feature = "bincode")]
impl SysvarSerialize for LastRestartSlot {}

#[cfg(test)]
mod tests {
use {super::*, crate::tests::to_bytes, serial_test::serial};

#[test]
#[serial]
fn test_last_restart_slot_get_uses_sysvar_syscall() {
let expected = LastRestartSlot {
last_restart_slot: 9999,
};
let data = to_bytes(&expected);
crate::tests::mock_get_sysvar_syscall(&data);

let got = LastRestartSlot::get().unwrap();
assert_eq!(got, expected);
}
}
34 changes: 34 additions & 0 deletions sysvar/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ pub trait SysvarSerialize:
/// Implements the [`Sysvar::get`] method for both SBF and host targets.
#[macro_export]
macro_rules! impl_sysvar_get {
// DEPRECATED: This variant is only for the deprecated Fees sysvar and should be
// removed once Fees is no longer in use. It uses the old-style direct syscall
// approach instead of the new sol_get_sysvar syscall.
($syscall_name:ident) => {
fn get() -> Result<Self, $crate::__private::ProgramError> {
let mut var = Self::default();
Expand All @@ -179,6 +182,24 @@ macro_rules! impl_sysvar_get {
}
}
};
($sysvar_id:expr) => {
fn get() -> Result<Self, $crate::__private::ProgramError> {
// Allocate uninitialized memory for the sysvar struct
let mut uninit = core::mem::MaybeUninit::<Self>::uninit();
let size = core::mem::size_of::<Self>() as u64;
// Safety: we build a mutable slice pointing to the uninitialized
// buffer. The `get_sysvar` syscall will fill exactly `size`
// bytes, after which the buffer is fully initialised.
let dst = unsafe {
core::slice::from_raw_parts_mut(uninit.as_mut_ptr() as *mut u8, size as usize)
};
// Attempt to load the sysvar data using the provided sysvar id.
$crate::get_sysvar(dst, &$sysvar_id, 0, size)?;
// Safety: `get_sysvar` succeeded and initialised the buffer.
let var = unsafe { uninit.assume_init() };
Ok(var)
}
};
}

/// Handler for retrieving a slice of sysvar data from the `sol_get_sysvar`
Expand Down Expand Up @@ -269,6 +290,19 @@ mod tests {
}));
}

/// Convert a value to its in-memory byte representation.
///
/// Safety: This relies on the type's plain old data layout. Intended for tests.
pub fn to_bytes<T>(value: &T) -> Vec<u8> {
unsafe {
let size = core::mem::size_of::<T>();
let ptr = (value as *const T) as *const u8;
let mut data = vec![0u8; size];
std::ptr::copy_nonoverlapping(ptr, data.as_mut_ptr(), size);
data
}
}

#[test]
fn test_sysvar_account_info_to_from() {
let test_sysvar = TestSysvar::default();
Expand Down
33 changes: 2 additions & 31 deletions sysvar/src/program_stubs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,32 +155,6 @@ pub(crate) fn sol_get_sysvar(
.sol_get_sysvar(sysvar_id_addr, var_addr, offset, length)
}

pub(crate) fn sol_get_clock_sysvar(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS.read().unwrap().sol_get_clock_sysvar(var_addr)
}

pub(crate) fn sol_get_epoch_schedule_sysvar(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS
.read()
.unwrap()
.sol_get_epoch_schedule_sysvar(var_addr)
}

pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS.read().unwrap().sol_get_fees_sysvar(var_addr)
}

pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr)
}

pub(crate) fn sol_get_last_restart_slot(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS
.read()
.unwrap()
.sol_get_last_restart_slot(var_addr)
}

pub fn sol_get_epoch_stake(vote_address: *const u8) -> u64 {
SYSCALL_STUBS
.read()
Expand Down Expand Up @@ -211,9 +185,6 @@ pub fn sol_get_stack_height() -> u64 {
SYSCALL_STUBS.read().unwrap().sol_get_stack_height()
}

pub(crate) fn sol_get_epoch_rewards_sysvar(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS
.read()
.unwrap()
.sol_get_epoch_rewards_sysvar(var_addr)
pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS.read().unwrap().sol_get_fees_sysvar(var_addr)
}
Loading
Loading