Skip to content

Commit

Permalink
implement sysvar get
Browse files Browse the repository at this point in the history
  • Loading branch information
2501babe committed Apr 5, 2024
1 parent 55c05c5 commit 2ae9fa0
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 155 deletions.
256 changes: 148 additions & 108 deletions program-runtime/src/sysvar_cache.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#[allow(deprecated)]
use solana_sdk::sysvar::{
fees::Fees, last_restart_slot::LastRestartSlot, recent_blockhashes::RecentBlockhashes,
};
use solana_sdk::sysvar::{fees::Fees, recent_blockhashes::RecentBlockhashes};
use {
crate::invoke_context::InvokeContext,
serde::de::DeserializeOwned,
solana_sdk::{
instruction::InstructionError,
pubkey::Pubkey,
sysvar::{
clock::Clock, epoch_rewards::EpochRewards, epoch_schedule::EpochSchedule, rent::Rent,
slot_hashes::SlotHashes, stake_history::StakeHistory, Sysvar, SysvarId,
clock::Clock, epoch_rewards::EpochRewards, epoch_schedule::EpochSchedule,
last_restart_slot::LastRestartSlot, rent::Rent, slot_hashes::SlotHashes,
stake_history::StakeHistory, Sysvar, SysvarId,
},
transaction_context::{IndexOfAccount, InstructionContext, TransactionContext},
},
Expand All @@ -26,189 +26,229 @@ impl ::solana_frozen_abi::abi_example::AbiExample for SysvarCache {

#[derive(Default, Clone, Debug)]
pub struct SysvarCache {
clock: Option<Arc<Clock>>,
epoch_schedule: Option<Arc<EpochSchedule>>,
epoch_rewards: Option<Arc<EpochRewards>>,
// full account data as provided by bank, including any trailing zeroes
// the setters MUST NOT be changed to serialize an object representation
// it is required that the syscall be able to access the full buffer
// TODO enforce this in tests
clock: Option<Vec<u8>>,
epoch_schedule: Option<Vec<u8>>,
epoch_rewards: Option<Vec<u8>>,
rent: Option<Vec<u8>>,
slot_hashes: Option<Vec<u8>>,
stake_history: Option<Vec<u8>>,
last_restart_slot: Option<Vec<u8>>,

// object representations of large sysvars for convenience
// these are used by the stake and vote builtin programs
// these should be removed once those programs are ported to bpf
slot_hashes_obj: Option<Arc<SlotHashes>>,
stake_history_obj: Option<Arc<StakeHistory>>,

// deprecated sysvars, these should be removed once practical
#[allow(deprecated)]
fees: Option<Arc<Fees>>,
rent: Option<Arc<Rent>>,
slot_hashes: Option<Arc<SlotHashes>>,
fees: Option<Fees>,
#[allow(deprecated)]
recent_blockhashes: Option<Arc<RecentBlockhashes>>,
stake_history: Option<Arc<StakeHistory>>,
last_restart_slot: Option<Arc<LastRestartSlot>>,
recent_blockhashes: Option<RecentBlockhashes>,
}

impl SysvarCache {
pub fn get_clock(&self) -> Result<Arc<Clock>, InstructionError> {
self.clock
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
}

pub fn set_clock(&mut self, clock: Clock) {
self.clock = Some(Arc::new(clock));
fn sysvar_id_to_buffer(&self, sysvar_id: &Pubkey) -> &Option<Vec<u8>> {
if *sysvar_id == Clock::id() {
&self.clock
} else if *sysvar_id == EpochSchedule::id() {
&self.epoch_schedule
} else if *sysvar_id == EpochRewards::id() {
&self.epoch_rewards
} else if *sysvar_id == Rent::id() {
&self.rent
} else if *sysvar_id == SlotHashes::id() {
&self.slot_hashes
} else if *sysvar_id == StakeHistory::id() {
&self.stake_history
} else if *sysvar_id == LastRestartSlot::id() {
&self.last_restart_slot
} else {
&None
}
}

pub fn get_epoch_schedule(&self) -> Result<Arc<EpochSchedule>, InstructionError> {
self.epoch_schedule
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
pub fn read_sysvar_into(
&self,
sysvar_id: &Pubkey,
length: usize,
offset: usize,
out_buf: &mut [u8],
) -> Result<(), InstructionError> {
if let Some(ref sysvar_buf) = self.sysvar_id_to_buffer(sysvar_id) {
if length == 0 {
panic!("zero length error");
}

match length.checked_add(offset) {
Some(limit) if limit <= sysvar_buf.len() => (),
_ => panic!("overrun error"),
}

if length != out_buf.len() {
panic!("bad out_buf error");
}

if let Some(sysvar_slice) = offset
.checked_add(length)
.and_then(|limit| sysvar_buf.get(offset..limit))
{
out_buf.copy_from_slice(sysvar_slice);
} else {
panic!("shouldnt happen");
}

Ok(())
} else {
panic!("not found err");
}
}

pub fn set_epoch_schedule(&mut self, epoch_schedule: EpochSchedule) {
self.epoch_schedule = Some(Arc::new(epoch_schedule));
// most if not all of the obj getter functions can be removed once builtins transition to bpf
fn get_sysvar_obj<T: DeserializeOwned>(
&self,
sysvar_id: &Pubkey,
) -> Result<T, InstructionError> {
if let Some(ref sysvar_buf) = self.sysvar_id_to_buffer(sysvar_id) {
bincode::deserialize(sysvar_buf).map_err(|_| InstructionError::UnsupportedSysvar)
} else {
Err(InstructionError::UnsupportedSysvar)
}
}

pub fn get_epoch_rewards(&self) -> Result<Arc<EpochRewards>, InstructionError> {
self.epoch_rewards
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
pub fn get_clock(&self) -> Result<Clock, InstructionError> {
self.get_sysvar_obj(&Clock::id())
}

pub fn set_epoch_rewards(&mut self, epoch_rewards: EpochRewards) {
self.epoch_rewards = Some(Arc::new(epoch_rewards));
pub fn get_epoch_schedule(&self) -> Result<EpochSchedule, InstructionError> {
self.get_sysvar_obj(&EpochSchedule::id())
}

#[deprecated]
#[allow(deprecated)]
pub fn get_fees(&self) -> Result<Arc<Fees>, InstructionError> {
self.fees.clone().ok_or(InstructionError::UnsupportedSysvar)
pub fn get_epoch_rewards(&self) -> Result<EpochRewards, InstructionError> {
self.get_sysvar_obj(&EpochRewards::id())
}

#[deprecated]
#[allow(deprecated)]
pub fn set_fees(&mut self, fees: Fees) {
self.fees = Some(Arc::new(fees));
pub fn get_rent(&self) -> Result<Rent, InstructionError> {
self.get_sysvar_obj(&Rent::id())
}

pub fn get_rent(&self) -> Result<Arc<Rent>, InstructionError> {
self.rent.clone().ok_or(InstructionError::UnsupportedSysvar)
pub fn get_last_restart_slot(&self) -> Result<LastRestartSlot, InstructionError> {
self.get_sysvar_obj(&LastRestartSlot::id())
}

pub fn set_rent(&mut self, rent: Rent) {
self.rent = Some(Arc::new(rent));
}

pub fn get_last_restart_slot(&self) -> Result<Arc<LastRestartSlot>, InstructionError> {
self.last_restart_slot
pub fn get_stake_history(&self) -> Result<Arc<StakeHistory>, InstructionError> {
self.stake_history_obj
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
}

pub fn set_last_restart_slot(&mut self, last_restart_slot: LastRestartSlot) {
self.last_restart_slot = Some(Arc::new(last_restart_slot));
}

pub fn get_slot_hashes(&self) -> Result<Arc<SlotHashes>, InstructionError> {
self.slot_hashes
self.slot_hashes_obj
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
}

pub fn set_slot_hashes(&mut self, slot_hashes: SlotHashes) {
self.slot_hashes = Some(Arc::new(slot_hashes));
}

#[deprecated]
#[allow(deprecated)]
pub fn get_recent_blockhashes(&self) -> Result<Arc<RecentBlockhashes>, InstructionError> {
self.recent_blockhashes
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
pub fn get_fees(&self) -> Result<Fees, InstructionError> {
self.fees.clone().ok_or(InstructionError::UnsupportedSysvar)
}

#[deprecated]
#[allow(deprecated)]
pub fn set_recent_blockhashes(&mut self, recent_blockhashes: RecentBlockhashes) {
self.recent_blockhashes = Some(Arc::new(recent_blockhashes));
}

pub fn get_stake_history(&self) -> Result<Arc<StakeHistory>, InstructionError> {
self.stake_history
pub fn get_recent_blockhashes(&self) -> Result<RecentBlockhashes, InstructionError> {
self.recent_blockhashes
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
}

pub fn set_stake_history(&mut self, stake_history: StakeHistory) {
self.stake_history = Some(Arc::new(stake_history));
}

pub fn fill_missing_entries<F: FnMut(&Pubkey, &mut dyn FnMut(&[u8]))>(
&mut self,
mut get_account_data: F,
) {
if self.clock.is_none() {
get_account_data(&Clock::id(), &mut |data: &[u8]| {
if let Ok(clock) = bincode::deserialize(data) {
self.set_clock(clock);
if bincode::deserialize::<Clock>(data).is_ok() {
self.clock = Some(data.to_vec());
}
});
}

if self.epoch_schedule.is_none() {
get_account_data(&EpochSchedule::id(), &mut |data: &[u8]| {
if let Ok(epoch_schedule) = bincode::deserialize(data) {
self.set_epoch_schedule(epoch_schedule);
if bincode::deserialize::<EpochSchedule>(data).is_ok() {
self.epoch_schedule = Some(data.to_vec());
}
});
}

if self.epoch_rewards.is_none() {
get_account_data(&EpochRewards::id(), &mut |data: &[u8]| {
if let Ok(epoch_rewards) = bincode::deserialize(data) {
self.set_epoch_rewards(epoch_rewards);
if bincode::deserialize::<EpochRewards>(data).is_ok() {
self.epoch_rewards = Some(data.to_vec());
}
});
}

#[allow(deprecated)]
if self.fees.is_none() {
get_account_data(&Fees::id(), &mut |data: &[u8]| {
if let Ok(fees) = bincode::deserialize(data) {
self.set_fees(fees);
}
});
}
if self.rent.is_none() {
get_account_data(&Rent::id(), &mut |data: &[u8]| {
if let Ok(rent) = bincode::deserialize(data) {
self.set_rent(rent);
if bincode::deserialize::<Rent>(data).is_ok() {
self.rent = Some(data.to_vec());
}
});
}

if self.slot_hashes.is_none() {
get_account_data(&SlotHashes::id(), &mut |data: &[u8]| {
if let Ok(slot_hashes) = bincode::deserialize(data) {
self.set_slot_hashes(slot_hashes);
}
});
}
#[allow(deprecated)]
if self.recent_blockhashes.is_none() {
get_account_data(&RecentBlockhashes::id(), &mut |data: &[u8]| {
if let Ok(recent_blockhashes) = bincode::deserialize(data) {
self.set_recent_blockhashes(recent_blockhashes);
if let Ok(obj) = bincode::deserialize::<SlotHashes>(data) {
self.slot_hashes = Some(data.to_vec());
self.slot_hashes_obj = Some(Arc::new(obj));
}
});
}

if self.stake_history.is_none() {
get_account_data(&StakeHistory::id(), &mut |data: &[u8]| {
if let Ok(stake_history) = bincode::deserialize(data) {
self.set_stake_history(stake_history);
if let Ok(obj) = bincode::deserialize::<StakeHistory>(data) {
self.stake_history = Some(data.to_vec());
self.stake_history_obj = Some(Arc::new(obj));
}
});
}

if self.last_restart_slot.is_none() {
get_account_data(&LastRestartSlot::id(), &mut |data: &[u8]| {
if let Ok(last_restart_slot) = bincode::deserialize(data) {
self.set_last_restart_slot(last_restart_slot);
if bincode::deserialize::<LastRestartSlot>(data).is_ok() {
self.last_restart_slot = Some(data.to_vec());
}
});
}

#[allow(deprecated)]
if self.fees.is_none() {
get_account_data(&Fees::id(), &mut |data: &[u8]| {
if let Ok(fees) = bincode::deserialize(data) {
self.fees = Some(fees);
}
});
}

#[allow(deprecated)]
if self.recent_blockhashes.is_none() {
get_account_data(&RecentBlockhashes::id(), &mut |data: &[u8]| {
if let Ok(recent_blockhashes) = bincode::deserialize(data) {
self.recent_blockhashes = Some(recent_blockhashes);
}
});
}
}

pub fn reset(&mut self) {
*self = SysvarCache::default();
*self = Self::default();
}
}

Expand Down Expand Up @@ -237,7 +277,7 @@ pub mod get_sysvar_with_account_check {
invoke_context: &InvokeContext,
instruction_context: &InstructionContext,
instruction_account_index: IndexOfAccount,
) -> Result<Arc<Clock>, InstructionError> {
) -> Result<Clock, InstructionError> {
check_sysvar_account::<Clock>(
invoke_context.transaction_context,
instruction_context,
Expand All @@ -250,7 +290,7 @@ pub mod get_sysvar_with_account_check {
invoke_context: &InvokeContext,
instruction_context: &InstructionContext,
instruction_account_index: IndexOfAccount,
) -> Result<Arc<Rent>, InstructionError> {
) -> Result<Rent, InstructionError> {
check_sysvar_account::<Rent>(
invoke_context.transaction_context,
instruction_context,
Expand All @@ -277,7 +317,7 @@ pub mod get_sysvar_with_account_check {
invoke_context: &InvokeContext,
instruction_context: &InstructionContext,
instruction_account_index: IndexOfAccount,
) -> Result<Arc<RecentBlockhashes>, InstructionError> {
) -> Result<RecentBlockhashes, InstructionError> {
check_sysvar_account::<RecentBlockhashes>(
invoke_context.transaction_context,
instruction_context,
Expand All @@ -303,7 +343,7 @@ pub mod get_sysvar_with_account_check {
invoke_context: &InvokeContext,
instruction_context: &InstructionContext,
instruction_account_index: IndexOfAccount,
) -> Result<Arc<LastRestartSlot>, InstructionError> {
) -> Result<LastRestartSlot, InstructionError> {
check_sysvar_account::<LastRestartSlot>(
invoke_context.transaction_context,
instruction_context,
Expand Down
2 changes: 1 addition & 1 deletion program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ macro_rules! processor {
}

fn get_sysvar<T: Default + Sysvar + Sized + serde::de::DeserializeOwned + Clone>(
sysvar: Result<Arc<T>, InstructionError>,
sysvar: Result<T, InstructionError>,
var_addr: *mut u8,
) -> u64 {
let invoke_context = get_invoke_context();
Expand Down
Loading

0 comments on commit 2ae9fa0

Please sign in to comment.