Skip to content
Merged
Changes from 1 commit
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
56 changes: 38 additions & 18 deletions sdk/src/sysvars/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ where

/// Load the number of instructions in the currently executing `Transaction`.
#[inline(always)]
pub fn num_instructions(&self) -> u16 {
pub fn num_instructions(&self) -> usize {
// SAFETY: The first 2 bytes of the Instructions sysvar data represents the
// number of instructions.
unsafe { u16::from_le_bytes(*(self.data.as_ptr() as *const [u8; 2])) }
u16::from_le_bytes(unsafe { *(self.data.as_ptr() as *const [u8; 2]) }) as usize
}

/// Load the current `Instruction`'s index in the currently executing
Expand Down Expand Up @@ -75,10 +75,7 @@ where
.as_ptr()
.add(size_of::<u16>() + index * size_of::<u16>()) as *const u16);

IntrospectedInstruction {
raw: self.data.as_ptr().add(offset as usize),
marker: PhantomData,
}
IntrospectedInstruction::new_unchecked(self.data.as_ptr().add(offset as usize))
}

/// Creates and returns an `IntrospectedInstruction` for the instruction at the specified index.
Expand All @@ -87,7 +84,7 @@ where
&self,
index: usize,
) -> Result<IntrospectedInstruction, ProgramError> {
if index >= self.num_instructions() as usize {
if index >= self.num_instructions() {
return Err(ProgramError::InvalidInstructionData);
}

Expand Down Expand Up @@ -133,10 +130,34 @@ impl<'a> TryFrom<&'a AccountView> for Instructions<Ref<'a, [u8]>> {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct IntrospectedInstruction<'a> {
pub raw: *const u8,
pub marker: PhantomData<&'a [u8]>,
marker: PhantomData<&'a [u8]>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

}

impl IntrospectedInstruction<'_> {
/// Create a new IntrospectedInstruction.
///
/// # Safety
///
/// This function is unsafe because it does not verify anything about the pointer.
///
/// It is private and used internally within the `get_instruction_account_at` function, which
/// performs the necessary index verification. However, to optimize performance for users
/// who are sure that the index is in bounds, we have exposed it as an unsafe function.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, this comment was copied from get_instruction_account_at_unchecked and should not be here

#[inline(always)]
unsafe fn new_unchecked(raw: *const u8) -> Self {
Self {
raw,
marker: PhantomData,
}
}

/// Load the number of instructions in the currently executing `Transaction`.
#[inline(always)]
pub fn num_account_metas(&self) -> usize {
// SAFETY: The first 2 bytes represent the number of accounts in the instruction.
u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) }) as usize
}

/// Get the instruction account at the specified index.
///
/// # Safety
Expand Down Expand Up @@ -166,9 +187,9 @@ impl IntrospectedInstruction<'_> {
index: usize,
) -> Result<&IntrospectedInstructionAccount, ProgramError> {
// SAFETY: The first 2 bytes represent the number of accounts in the instruction.
let num_accounts = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) });
let num_accounts = self.num_account_metas();

if index >= num_accounts as usize {
if index >= num_accounts {
return Err(ProgramError::InvalidArgument);
}

Expand All @@ -180,24 +201,23 @@ impl IntrospectedInstruction<'_> {
#[inline(always)]
pub fn get_program_id(&self) -> &Address {
// SAFETY: The first 2 bytes represent the number of accounts in the instruction.
let num_accounts = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) });
let num_accounts = self.num_account_metas();

// SAFETY: The program ID is located after the instruction accounts.
unsafe {
&*(self.raw.add(
size_of::<u16>()
+ num_accounts as usize * size_of::<IntrospectedInstructionAccount>(),
) as *const Address)
&*(self
.raw
.add(size_of::<u16>() + num_accounts * size_of::<IntrospectedInstructionAccount>())
as *const Address)
}
}

/// Get the instruction data of the `Instruction`.
#[inline(always)]
pub fn get_instruction_data(&self) -> &[u8] {
// SAFETY: The first 2 bytes represent the number of accounts in the instruction.
let offset = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) }) as usize
* size_of::<IntrospectedInstructionAccount>()
+ ADDRESS_BYTES;
let offset =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bit in particular looks much nicer!

self.num_account_metas() * size_of::<IntrospectedInstructionAccount>() + ADDRESS_BYTES;

// SAFETY: The instruction data length is located after the program ID.
let data_len = u16::from_le_bytes(unsafe {
Expand Down
Loading