use {
super::*, crate::translate_mut,
solana_program_runtime::execution_budget::SVMTransactionExecutionCost, solana_sbpf::ebpf,
};
fn get_sysvar<T: std::fmt::Debug + SysvarSerialize + Clone>(
sysvar: Result<Arc<T>, InstructionError>,
var_addr: u64,
check_aligned: bool,
memory_mapping: &mut MemoryMapping,
invoke_context: &mut InvokeContext,
) -> Result<u64, Error> {
consume_compute_meter(
invoke_context,
invoke_context
.get_execution_cost()
.sysvar_base_cost
.saturating_add(size_of::<T>() as u64),
)?;
if var_addr >= ebpf::MM_INPUT_START
&& invoke_context
.get_feature_set()
.syscall_parameter_address_restrictions
{
return Err(SyscallError::InvalidPointer.into());
}
translate_mut!(
memory_mapping,
check_aligned,
let var: &mut T = map(var_addr)?;
);
let sysvar: Arc<T> = sysvar?;
*var = T::clone(sysvar.as_ref());
Ok(SUCCESS)
}
declare_builtin_function!(
SyscallGetClockSysvar,
fn rust(
invoke_context: &mut InvokeContext,
var_addr: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
get_sysvar(
invoke_context.get_sysvar_cache().get_clock(),
var_addr,
invoke_context.get_check_aligned(),
memory_mapping,
invoke_context,
)
}
);
declare_builtin_function!(
SyscallGetEpochScheduleSysvar,
fn rust(
invoke_context: &mut InvokeContext,
var_addr: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
get_sysvar(
invoke_context.get_sysvar_cache().get_epoch_schedule(),
var_addr,
invoke_context.get_check_aligned(),
memory_mapping,
invoke_context,
)
}
);
declare_builtin_function!(
SyscallGetEpochRewardsSysvar,
fn rust(
invoke_context: &mut InvokeContext,
var_addr: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
get_sysvar(
invoke_context.get_sysvar_cache().get_epoch_rewards(),
var_addr,
invoke_context.get_check_aligned(),
memory_mapping,
invoke_context,
)
}
);
declare_builtin_function!(
SyscallGetFeesSysvar,
fn rust(
invoke_context: &mut InvokeContext,
var_addr: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
#[allow(deprecated)]
{
get_sysvar(
invoke_context.get_sysvar_cache().get_fees(),
var_addr,
invoke_context.get_check_aligned(),
memory_mapping,
invoke_context,
)
}
}
);
declare_builtin_function!(
SyscallGetRentSysvar,
fn rust(
invoke_context: &mut InvokeContext,
var_addr: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
get_sysvar(
invoke_context.get_sysvar_cache().get_rent(),
var_addr,
invoke_context.get_check_aligned(),
memory_mapping,
invoke_context,
)
}
);
declare_builtin_function!(
SyscallGetLastRestartSlotSysvar,
fn rust(
invoke_context: &mut InvokeContext,
var_addr: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
get_sysvar(
invoke_context.get_sysvar_cache().get_last_restart_slot(),
var_addr,
invoke_context.get_check_aligned(),
memory_mapping,
invoke_context,
)
}
);
const SYSVAR_NOT_FOUND: u64 = 2;
const OFFSET_LENGTH_EXCEEDS_SYSVAR: u64 = 1;
declare_builtin_function!(
SyscallGetSysvar,
fn rust(
invoke_context: &mut InvokeContext,
sysvar_id_addr: u64,
var_addr: u64,
offset: u64,
length: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let check_aligned = invoke_context.get_check_aligned();
let SVMTransactionExecutionCost {
sysvar_base_cost,
cpi_bytes_per_unit,
mem_op_base_cost,
..
} = *invoke_context.get_execution_cost();
let sysvar_id_cost = 32_u64.checked_div(cpi_bytes_per_unit).unwrap_or(0);
let sysvar_buf_cost = length.checked_div(cpi_bytes_per_unit).unwrap_or(0);
consume_compute_meter(
invoke_context,
sysvar_base_cost
.saturating_add(sysvar_id_cost)
.saturating_add(std::cmp::max(sysvar_buf_cost, mem_op_base_cost)),
)?;
if var_addr >= ebpf::MM_INPUT_START
&& invoke_context
.get_feature_set()
.syscall_parameter_address_restrictions
{
return Err(SyscallError::InvalidPointer.into());
}
translate_mut!(
memory_mapping,
check_aligned,
let var: &mut [u8] = map(var_addr, length)?;
);
let sysvar_id = translate_type::<Pubkey>(memory_mapping, sysvar_id_addr, check_aligned)?;
let offset_length = offset
.checked_add(length)
.ok_or(InstructionError::ArithmeticOverflow)?;
let _ = var_addr
.checked_add(length)
.ok_or(InstructionError::ArithmeticOverflow)?;
let cache = invoke_context.get_sysvar_cache();
let Some(sysvar_buf) = cache.sysvar_id_to_buffer(sysvar_id) else { return Ok(SYSVAR_NOT_FOUND) };
if let Some(sysvar_slice) = sysvar_buf.get(offset as usize..offset_length as usize) {
var.copy_from_slice(sysvar_slice);
} else {
return Ok(OFFSET_LENGTH_EXCEEDS_SYSVAR);
}
Ok(SUCCESS)
}
);