use crate::{ account_info::{AccountInfo, Ref}, instruction::AccountMeta, program_error::ProgramError, pubkey::{Pubkey, PUBKEY_BYTES}, }; use core::{marker::PhantomData, mem::size_of, ops::Deref}; /// Instructions sysvar ID `Sysvar1nstructions1111111111111111111111111`. pub const INSTRUCTIONS_ID: Pubkey = [ 0x06, 0xa7, 0xd5, 0x17, 0x18, 0x7b, 0xd1, 0x66, 0x35, 0xda, 0xd4, 0x04, 0x55, 0xfd, 0xc2, 0xc0, 0xc1, 0x24, 0xc6, 0x8f, 0x21, 0x56, 0x75, 0xa5, 0xdb, 0xba, 0xcb, 0x5f, 0x08, 0x00, 0x00, 0x00, ]; pub struct Instructions where T: Deref, { data: T, } impl Instructions where T: Deref, { /// Creates a new `Instructions` struct. /// /// `data` is the instructions sysvar account data. /// /// # Safety /// /// This function is unsafe because it does not check if the provided data is from the Sysvar Account. #[inline(always)] pub unsafe fn new_unchecked(data: T) -> Self { Instructions { data } } /// Load the number of instructions in the currently executing `Transaction`. #[inline(always)] pub fn num_instructions(&self) -> u16 { // 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])) } } /// Load the current `Instruction`'s index in the currently executing /// `Transaction`. #[inline(always)] pub fn load_current_index(&self) -> u16 { let len = self.data.len(); // SAFETY: The last 2 bytes of the Instructions sysvar data represents the current // instruction index. unsafe { u16::from_le_bytes(*(self.data.as_ptr().add(len - 2) as *const [u8; 2])) } } /// Creates and returns an `IntrospectedInstruction` for the instruction at the specified index. /// /// # Safety /// /// This function is unsafe because it does not check if the provided index is out of bounds. It is /// typically used internally with the `load_instruction_at` or `get_instruction_relative` functions, /// which perform the necessary index verification. #[inline(always)] pub unsafe fn deserialize_instruction_unchecked( &self, index: usize, ) -> IntrospectedInstruction { let offset = *(self .data .as_ptr() .add(size_of::() + index * size_of::()) as *const u16); IntrospectedInstruction { raw: self.data.as_ptr().add(offset as usize), marker: PhantomData, } } /// Creates and returns an `IntrospectedInstruction` for the instruction at the specified index. #[inline(always)] pub fn load_instruction_at( &self, index: usize, ) -> Result { if index >= self.num_instructions() as usize { return Err(ProgramError::InvalidInstructionData); } // SAFETY: The index was checked to be in bounds. Ok(unsafe { self.deserialize_instruction_unchecked(index) }) } /// Creates and returns an `IntrospectedInstruction` relative to the current `Instruction` in the /// currently executing `Transaction. #[inline(always)] pub fn get_instruction_relative( &self, index_relative_to_current: i64, ) -> Result { let current_index = self.load_current_index() as i64; let index = current_index.saturating_add(index_relative_to_current); if index < 0 { return Err(ProgramError::InvalidInstructionData); } self.load_instruction_at(index as usize) } } impl<'a> TryFrom<&'a AccountInfo> for Instructions> { type Error = ProgramError; #[inline(always)] fn try_from(account_info: &'a AccountInfo) -> Result { if account_info.key() != &INSTRUCTIONS_ID { return Err(ProgramError::UnsupportedSysvar); } Ok(Instructions { data: account_info.try_borrow_data()?, }) } } #[repr(C)] #[derive(Clone, PartialEq, Eq)] pub struct IntrospectedInstruction<'a> { pub raw: *const u8, pub marker: PhantomData<&'a [u8]>, } impl IntrospectedInstruction<'_> { /// Get the account meta at the specified index. /// /// # Safety /// /// This function is unsafe because it does not verify if the index is out of bounds. /// /// It is typically used internally within the `get_account_meta_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. #[inline(always)] pub unsafe fn get_account_meta_at_unchecked(&self, index: usize) -> &IntrospectedAccountMeta { let offset = core::mem::size_of::() + (index * IntrospectedAccountMeta::LEN); &*(self.raw.add(offset) as *const IntrospectedAccountMeta) } /// Get the account meta at the specified index. /// /// # Errors /// /// Returns [`ProgramError::InvalidArgument`] if the index is out of bounds. #[inline(always)] pub fn get_account_meta_at( &self, index: usize, ) -> Result<&IntrospectedAccountMeta, 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]) }); if index >= num_accounts as usize { return Err(ProgramError::InvalidArgument); } // SAFETY: The index was checked to be in bounds. Ok(unsafe { self.get_account_meta_at_unchecked(index) }) } /// Get the program ID of the `Instruction`. #[inline(always)] pub fn get_program_id(&self) -> &Pubkey { // 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]) }); // SAFETY: The program ID is located after the account metas. unsafe { &*(self.raw.add( size_of::() + num_accounts as usize * size_of::(), ) as *const Pubkey) } } /// 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::() + PUBKEY_BYTES; // SAFETY: The instruction data length is located after the program ID. let data_len = u16::from_le_bytes(unsafe { *(self.raw.add(size_of::() + offset) as *const [u8; 2]) }); // SAFETY: The instruction data is located after the data length. unsafe { core::slice::from_raw_parts( self.raw.add(size_of::() + offset + size_of::()), data_len as usize, ) } } } /// The bit positions for the signer flags in the `AccountMeta`. const IS_SIGNER: u8 = 0b00000001; /// The bit positions for the writable flags in the `AccountMeta`. const IS_WRITABLE: u8 = 0b00000010; #[repr(C)] #[derive(Clone, PartialEq, Eq)] pub struct IntrospectedAccountMeta { /// Account flags: /// * bit `0`: signer /// * bit `1`: writable flags: u8, /// The account key. pub key: Pubkey, } impl IntrospectedAccountMeta { const LEN: usize = core::mem::size_of::(); /// Indicate whether the account is writable or not. #[inline(always)] pub fn is_writable(&self) -> bool { (self.flags & IS_WRITABLE) != 0 } /// Indicate whether the account is a signer or not. #[inline(always)] pub fn is_signer(&self) -> bool { (self.flags & IS_SIGNER) != 0 } /// Convert the `IntrospectedAccountMeta` to an `AccountMeta`. #[inline(always)] pub fn to_account_meta(&self) -> AccountMeta { AccountMeta::new(&self.key, self.is_writable(), self.is_signer()) } }