| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- 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<T>
- where
- T: Deref<Target = [u8]>,
- {
- data: T,
- }
- impl<T> Instructions<T>
- where
- T: Deref<Target = [u8]>,
- {
- /// 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::<u16>() + index * size_of::<u16>()) 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<IntrospectedInstruction, ProgramError> {
- 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<IntrospectedInstruction, ProgramError> {
- 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<Ref<'a, [u8]>> {
- type Error = ProgramError;
- #[inline(always)]
- fn try_from(account_info: &'a AccountInfo) -> Result<Self, Self::Error> {
- 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::<u16>() + (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::<u16>() + num_accounts as usize * size_of::<IntrospectedAccountMeta>(),
- ) 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::<IntrospectedAccountMeta>()
- + 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::<u16>() + 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::<u16>() + offset + size_of::<u16>()),
- 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::<Self>();
- /// 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())
- }
- }
|